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     this.parent = false;
8432     
8433     if (typeof(depreciated_tpl) == 'undefined') {
8434         // new way.. - universal constructor.
8435         Roo.apply(this, config);
8436         this.el  = Roo.get(this.el);
8437     } else {
8438         // old format..
8439         this.el  = Roo.get(config);
8440         this.tpl = depreciated_tpl;
8441         Roo.apply(this, depreciated_config);
8442     }
8443     this.wrapEl  = this.el.wrap().wrap();
8444     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8445     
8446     
8447     if(typeof(this.tpl) == "string"){
8448         this.tpl = new Roo.Template(this.tpl);
8449     } else {
8450         // support xtype ctors..
8451         this.tpl = new Roo.factory(this.tpl, Roo);
8452     }
8453     
8454     
8455     this.tpl.compile();
8456     
8457     /** @private */
8458     this.addEvents({
8459         /**
8460          * @event beforeclick
8461          * Fires before a click is processed. Returns false to cancel the default action.
8462          * @param {Roo.View} this
8463          * @param {Number} index The index of the target node
8464          * @param {HTMLElement} node The target node
8465          * @param {Roo.EventObject} e The raw event object
8466          */
8467             "beforeclick" : true,
8468         /**
8469          * @event click
8470          * Fires when a template node is clicked.
8471          * @param {Roo.View} this
8472          * @param {Number} index The index of the target node
8473          * @param {HTMLElement} node The target node
8474          * @param {Roo.EventObject} e The raw event object
8475          */
8476             "click" : true,
8477         /**
8478          * @event dblclick
8479          * Fires when a template node is double clicked.
8480          * @param {Roo.View} this
8481          * @param {Number} index The index of the target node
8482          * @param {HTMLElement} node The target node
8483          * @param {Roo.EventObject} e The raw event object
8484          */
8485             "dblclick" : true,
8486         /**
8487          * @event contextmenu
8488          * Fires when a template node is right clicked.
8489          * @param {Roo.View} this
8490          * @param {Number} index The index of the target node
8491          * @param {HTMLElement} node The target node
8492          * @param {Roo.EventObject} e The raw event object
8493          */
8494             "contextmenu" : true,
8495         /**
8496          * @event selectionchange
8497          * Fires when the selected nodes change.
8498          * @param {Roo.View} this
8499          * @param {Array} selections Array of the selected nodes
8500          */
8501             "selectionchange" : true,
8502     
8503         /**
8504          * @event beforeselect
8505          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8506          * @param {Roo.View} this
8507          * @param {HTMLElement} node The node to be selected
8508          * @param {Array} selections Array of currently selected nodes
8509          */
8510             "beforeselect" : true,
8511         /**
8512          * @event preparedata
8513          * Fires on every row to render, to allow you to change the data.
8514          * @param {Roo.View} this
8515          * @param {Object} data to be rendered (change this)
8516          */
8517           "preparedata" : true
8518           
8519           
8520         });
8521
8522
8523
8524     this.el.on({
8525         "click": this.onClick,
8526         "dblclick": this.onDblClick,
8527         "contextmenu": this.onContextMenu,
8528         scope:this
8529     });
8530
8531     this.selections = [];
8532     this.nodes = [];
8533     this.cmp = new Roo.CompositeElementLite([]);
8534     if(this.store){
8535         this.store = Roo.factory(this.store, Roo.data);
8536         this.setStore(this.store, true);
8537     }
8538     
8539     if ( this.footer && this.footer.xtype) {
8540            
8541          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8542         
8543         this.footer.dataSource = this.store
8544         this.footer.container = fctr;
8545         this.footer = Roo.factory(this.footer, Roo);
8546         fctr.insertFirst(this.el);
8547         
8548         // this is a bit insane - as the paging toolbar seems to detach the el..
8549 //        dom.parentNode.parentNode.parentNode
8550          // they get detached?
8551     }
8552     
8553     
8554     Roo.View.superclass.constructor.call(this);
8555     
8556     
8557 };
8558
8559 Roo.extend(Roo.View, Roo.util.Observable, {
8560     
8561      /**
8562      * @cfg {Roo.data.Store} store Data store to load data from.
8563      */
8564     store : false,
8565     
8566     /**
8567      * @cfg {String|Roo.Element} el The container element.
8568      */
8569     el : '',
8570     
8571     /**
8572      * @cfg {String|Roo.Template} tpl The template used by this View 
8573      */
8574     tpl : false,
8575     /**
8576      * @cfg {String} dataName the named area of the template to use as the data area
8577      *                          Works with domtemplates roo-name="name"
8578      */
8579     dataName: false,
8580     /**
8581      * @cfg {String} selectedClass The css class to add to selected nodes
8582      */
8583     selectedClass : "x-view-selected",
8584      /**
8585      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8586      */
8587     emptyText : "",
8588     
8589     /**
8590      * @cfg {String} text to display on mask (default Loading)
8591      */
8592     mask : false,
8593     /**
8594      * @cfg {Boolean} multiSelect Allow multiple selection
8595      */
8596     multiSelect : false,
8597     /**
8598      * @cfg {Boolean} singleSelect Allow single selection
8599      */
8600     singleSelect:  false,
8601     
8602     /**
8603      * @cfg {Boolean} toggleSelect - selecting 
8604      */
8605     toggleSelect : false,
8606     
8607     /**
8608      * @cfg {Boolean} tickable - selecting 
8609      */
8610     tickable : false,
8611     
8612     /**
8613      * Returns the element this view is bound to.
8614      * @return {Roo.Element}
8615      */
8616     getEl : function(){
8617         return this.wrapEl;
8618     },
8619     
8620     
8621
8622     /**
8623      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8624      */
8625     refresh : function(){
8626         Roo.log('refresh');
8627         var t = this.tpl;
8628         
8629         // if we are using something like 'domtemplate', then
8630         // the what gets used is:
8631         // t.applySubtemplate(NAME, data, wrapping data..)
8632         // the outer template then get' applied with
8633         //     the store 'extra data'
8634         // and the body get's added to the
8635         //      roo-name="data" node?
8636         //      <span class='roo-tpl-{name}'></span> ?????
8637         
8638         
8639         
8640         this.clearSelections();
8641         this.el.update("");
8642         var html = [];
8643         var records = this.store.getRange();
8644         if(records.length < 1) {
8645             
8646             // is this valid??  = should it render a template??
8647             
8648             this.el.update(this.emptyText);
8649             return;
8650         }
8651         var el = this.el;
8652         if (this.dataName) {
8653             this.el.update(t.apply(this.store.meta)); //????
8654             el = this.el.child('.roo-tpl-' + this.dataName);
8655         }
8656         
8657         for(var i = 0, len = records.length; i < len; i++){
8658             var data = this.prepareData(records[i].data, i, records[i]);
8659             this.fireEvent("preparedata", this, data, i, records[i]);
8660             
8661             var d = Roo.apply({}, data);
8662             
8663             if(this.tickable){
8664                 Roo.apply(d, {'roo-id' : Roo.id()});
8665                 
8666                 var _this = this;
8667             
8668                 Roo.each(this.parent.item, function(item){
8669                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8670                         return;
8671                     }
8672                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8673                 });
8674             }
8675             
8676             html[html.length] = Roo.util.Format.trim(
8677                 this.dataName ?
8678                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8679                     t.apply(d)
8680             );
8681         }
8682         
8683         
8684         
8685         el.update(html.join(""));
8686         this.nodes = el.dom.childNodes;
8687         this.updateIndexes(0);
8688     },
8689     
8690
8691     /**
8692      * Function to override to reformat the data that is sent to
8693      * the template for each node.
8694      * DEPRICATED - use the preparedata event handler.
8695      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8696      * a JSON object for an UpdateManager bound view).
8697      */
8698     prepareData : function(data, index, record)
8699     {
8700         this.fireEvent("preparedata", this, data, index, record);
8701         return data;
8702     },
8703
8704     onUpdate : function(ds, record){
8705          Roo.log('on update');   
8706         this.clearSelections();
8707         var index = this.store.indexOf(record);
8708         var n = this.nodes[index];
8709         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8710         n.parentNode.removeChild(n);
8711         this.updateIndexes(index, index);
8712     },
8713
8714     
8715     
8716 // --------- FIXME     
8717     onAdd : function(ds, records, index)
8718     {
8719         Roo.log(['on Add', ds, records, index] );        
8720         this.clearSelections();
8721         if(this.nodes.length == 0){
8722             this.refresh();
8723             return;
8724         }
8725         var n = this.nodes[index];
8726         for(var i = 0, len = records.length; i < len; i++){
8727             var d = this.prepareData(records[i].data, i, records[i]);
8728             if(n){
8729                 this.tpl.insertBefore(n, d);
8730             }else{
8731                 
8732                 this.tpl.append(this.el, d);
8733             }
8734         }
8735         this.updateIndexes(index);
8736     },
8737
8738     onRemove : function(ds, record, index){
8739         Roo.log('onRemove');
8740         this.clearSelections();
8741         var el = this.dataName  ?
8742             this.el.child('.roo-tpl-' + this.dataName) :
8743             this.el; 
8744         
8745         el.dom.removeChild(this.nodes[index]);
8746         this.updateIndexes(index);
8747     },
8748
8749     /**
8750      * Refresh an individual node.
8751      * @param {Number} index
8752      */
8753     refreshNode : function(index){
8754         this.onUpdate(this.store, this.store.getAt(index));
8755     },
8756
8757     updateIndexes : function(startIndex, endIndex){
8758         var ns = this.nodes;
8759         startIndex = startIndex || 0;
8760         endIndex = endIndex || ns.length - 1;
8761         for(var i = startIndex; i <= endIndex; i++){
8762             ns[i].nodeIndex = i;
8763         }
8764     },
8765
8766     /**
8767      * Changes the data store this view uses and refresh the view.
8768      * @param {Store} store
8769      */
8770     setStore : function(store, initial){
8771         if(!initial && this.store){
8772             this.store.un("datachanged", this.refresh);
8773             this.store.un("add", this.onAdd);
8774             this.store.un("remove", this.onRemove);
8775             this.store.un("update", this.onUpdate);
8776             this.store.un("clear", this.refresh);
8777             this.store.un("beforeload", this.onBeforeLoad);
8778             this.store.un("load", this.onLoad);
8779             this.store.un("loadexception", this.onLoad);
8780         }
8781         if(store){
8782           
8783             store.on("datachanged", this.refresh, this);
8784             store.on("add", this.onAdd, this);
8785             store.on("remove", this.onRemove, this);
8786             store.on("update", this.onUpdate, this);
8787             store.on("clear", this.refresh, this);
8788             store.on("beforeload", this.onBeforeLoad, this);
8789             store.on("load", this.onLoad, this);
8790             store.on("loadexception", this.onLoad, this);
8791         }
8792         
8793         if(store){
8794             this.refresh();
8795         }
8796     },
8797     /**
8798      * onbeforeLoad - masks the loading area.
8799      *
8800      */
8801     onBeforeLoad : function(store,opts)
8802     {
8803          Roo.log('onBeforeLoad');   
8804         if (!opts.add) {
8805             this.el.update("");
8806         }
8807         this.el.mask(this.mask ? this.mask : "Loading" ); 
8808     },
8809     onLoad : function ()
8810     {
8811         this.el.unmask();
8812     },
8813     
8814
8815     /**
8816      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8817      * @param {HTMLElement} node
8818      * @return {HTMLElement} The template node
8819      */
8820     findItemFromChild : function(node){
8821         var el = this.dataName  ?
8822             this.el.child('.roo-tpl-' + this.dataName,true) :
8823             this.el.dom; 
8824         
8825         if(!node || node.parentNode == el){
8826                     return node;
8827             }
8828             var p = node.parentNode;
8829             while(p && p != el){
8830             if(p.parentNode == el){
8831                 return p;
8832             }
8833             p = p.parentNode;
8834         }
8835             return null;
8836     },
8837
8838     /** @ignore */
8839     onClick : function(e){
8840         var item = this.findItemFromChild(e.getTarget());
8841         if(item){
8842             var index = this.indexOf(item);
8843             if(this.onItemClick(item, index, e) !== false){
8844                 this.fireEvent("click", this, index, item, e);
8845             }
8846         }else{
8847             this.clearSelections();
8848         }
8849     },
8850
8851     /** @ignore */
8852     onContextMenu : function(e){
8853         var item = this.findItemFromChild(e.getTarget());
8854         if(item){
8855             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8856         }
8857     },
8858
8859     /** @ignore */
8860     onDblClick : function(e){
8861         var item = this.findItemFromChild(e.getTarget());
8862         if(item){
8863             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8864         }
8865     },
8866
8867     onItemClick : function(item, index, e)
8868     {
8869         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8870             return false;
8871         }
8872         if (this.toggleSelect) {
8873             var m = this.isSelected(item) ? 'unselect' : 'select';
8874             Roo.log(m);
8875             var _t = this;
8876             _t[m](item, true, false);
8877             return true;
8878         }
8879         if(this.multiSelect || this.singleSelect){
8880             if(this.multiSelect && e.shiftKey && this.lastSelection){
8881                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8882             }else{
8883                 this.select(item, this.multiSelect && e.ctrlKey);
8884                 this.lastSelection = item;
8885             }
8886             
8887             if(!this.tickable){
8888                 e.preventDefault();
8889             }
8890             
8891         }
8892         return true;
8893     },
8894
8895     /**
8896      * Get the number of selected nodes.
8897      * @return {Number}
8898      */
8899     getSelectionCount : function(){
8900         return this.selections.length;
8901     },
8902
8903     /**
8904      * Get the currently selected nodes.
8905      * @return {Array} An array of HTMLElements
8906      */
8907     getSelectedNodes : function(){
8908         return this.selections;
8909     },
8910
8911     /**
8912      * Get the indexes of the selected nodes.
8913      * @return {Array}
8914      */
8915     getSelectedIndexes : function(){
8916         var indexes = [], s = this.selections;
8917         for(var i = 0, len = s.length; i < len; i++){
8918             indexes.push(s[i].nodeIndex);
8919         }
8920         return indexes;
8921     },
8922
8923     /**
8924      * Clear all selections
8925      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8926      */
8927     clearSelections : function(suppressEvent){
8928         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8929             this.cmp.elements = this.selections;
8930             this.cmp.removeClass(this.selectedClass);
8931             this.selections = [];
8932             if(!suppressEvent){
8933                 this.fireEvent("selectionchange", this, this.selections);
8934             }
8935         }
8936     },
8937
8938     /**
8939      * Returns true if the passed node is selected
8940      * @param {HTMLElement/Number} node The node or node index
8941      * @return {Boolean}
8942      */
8943     isSelected : function(node){
8944         var s = this.selections;
8945         if(s.length < 1){
8946             return false;
8947         }
8948         node = this.getNode(node);
8949         return s.indexOf(node) !== -1;
8950     },
8951
8952     /**
8953      * Selects nodes.
8954      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8955      * @param {Boolean} keepExisting (optional) true to keep existing selections
8956      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8957      */
8958     select : function(nodeInfo, keepExisting, suppressEvent){
8959         if(nodeInfo instanceof Array){
8960             if(!keepExisting){
8961                 this.clearSelections(true);
8962             }
8963             for(var i = 0, len = nodeInfo.length; i < len; i++){
8964                 this.select(nodeInfo[i], true, true);
8965             }
8966             return;
8967         } 
8968         var node = this.getNode(nodeInfo);
8969         if(!node || this.isSelected(node)){
8970             return; // already selected.
8971         }
8972         if(!keepExisting){
8973             this.clearSelections(true);
8974         }
8975         
8976         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8977             Roo.fly(node).addClass(this.selectedClass);
8978             this.selections.push(node);
8979             if(!suppressEvent){
8980                 this.fireEvent("selectionchange", this, this.selections);
8981             }
8982         }
8983         
8984         
8985     },
8986       /**
8987      * Unselects nodes.
8988      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8989      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8990      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8991      */
8992     unselect : function(nodeInfo, keepExisting, suppressEvent)
8993     {
8994         if(nodeInfo instanceof Array){
8995             Roo.each(this.selections, function(s) {
8996                 this.unselect(s, nodeInfo);
8997             }, this);
8998             return;
8999         }
9000         var node = this.getNode(nodeInfo);
9001         if(!node || !this.isSelected(node)){
9002             Roo.log("not selected");
9003             return; // not selected.
9004         }
9005         // fireevent???
9006         var ns = [];
9007         Roo.each(this.selections, function(s) {
9008             if (s == node ) {
9009                 Roo.fly(node).removeClass(this.selectedClass);
9010
9011                 return;
9012             }
9013             ns.push(s);
9014         },this);
9015         
9016         this.selections= ns;
9017         this.fireEvent("selectionchange", this, this.selections);
9018     },
9019
9020     /**
9021      * Gets a template node.
9022      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9023      * @return {HTMLElement} The node or null if it wasn't found
9024      */
9025     getNode : function(nodeInfo){
9026         if(typeof nodeInfo == "string"){
9027             return document.getElementById(nodeInfo);
9028         }else if(typeof nodeInfo == "number"){
9029             return this.nodes[nodeInfo];
9030         }
9031         return nodeInfo;
9032     },
9033
9034     /**
9035      * Gets a range template nodes.
9036      * @param {Number} startIndex
9037      * @param {Number} endIndex
9038      * @return {Array} An array of nodes
9039      */
9040     getNodes : function(start, end){
9041         var ns = this.nodes;
9042         start = start || 0;
9043         end = typeof end == "undefined" ? ns.length - 1 : end;
9044         var nodes = [];
9045         if(start <= end){
9046             for(var i = start; i <= end; i++){
9047                 nodes.push(ns[i]);
9048             }
9049         } else{
9050             for(var i = start; i >= end; i--){
9051                 nodes.push(ns[i]);
9052             }
9053         }
9054         return nodes;
9055     },
9056
9057     /**
9058      * Finds the index of the passed node
9059      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9060      * @return {Number} The index of the node or -1
9061      */
9062     indexOf : function(node){
9063         node = this.getNode(node);
9064         if(typeof node.nodeIndex == "number"){
9065             return node.nodeIndex;
9066         }
9067         var ns = this.nodes;
9068         for(var i = 0, len = ns.length; i < len; i++){
9069             if(ns[i] == node){
9070                 return i;
9071             }
9072         }
9073         return -1;
9074     }
9075 });
9076 /*
9077  * Based on:
9078  * Ext JS Library 1.1.1
9079  * Copyright(c) 2006-2007, Ext JS, LLC.
9080  *
9081  * Originally Released Under LGPL - original licence link has changed is not relivant.
9082  *
9083  * Fork - LGPL
9084  * <script type="text/javascript">
9085  */
9086
9087 /**
9088  * @class Roo.JsonView
9089  * @extends Roo.View
9090  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9091 <pre><code>
9092 var view = new Roo.JsonView({
9093     container: "my-element",
9094     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9095     multiSelect: true, 
9096     jsonRoot: "data" 
9097 });
9098
9099 // listen for node click?
9100 view.on("click", function(vw, index, node, e){
9101     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9102 });
9103
9104 // direct load of JSON data
9105 view.load("foobar.php");
9106
9107 // Example from my blog list
9108 var tpl = new Roo.Template(
9109     '&lt;div class="entry"&gt;' +
9110     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9111     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9112     "&lt;/div&gt;&lt;hr /&gt;"
9113 );
9114
9115 var moreView = new Roo.JsonView({
9116     container :  "entry-list", 
9117     template : tpl,
9118     jsonRoot: "posts"
9119 });
9120 moreView.on("beforerender", this.sortEntries, this);
9121 moreView.load({
9122     url: "/blog/get-posts.php",
9123     params: "allposts=true",
9124     text: "Loading Blog Entries..."
9125 });
9126 </code></pre>
9127
9128 * Note: old code is supported with arguments : (container, template, config)
9129
9130
9131  * @constructor
9132  * Create a new JsonView
9133  * 
9134  * @param {Object} config The config object
9135  * 
9136  */
9137 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9138     
9139     
9140     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9141
9142     var um = this.el.getUpdateManager();
9143     um.setRenderer(this);
9144     um.on("update", this.onLoad, this);
9145     um.on("failure", this.onLoadException, this);
9146
9147     /**
9148      * @event beforerender
9149      * Fires before rendering of the downloaded JSON data.
9150      * @param {Roo.JsonView} this
9151      * @param {Object} data The JSON data loaded
9152      */
9153     /**
9154      * @event load
9155      * Fires when data is loaded.
9156      * @param {Roo.JsonView} this
9157      * @param {Object} data The JSON data loaded
9158      * @param {Object} response The raw Connect response object
9159      */
9160     /**
9161      * @event loadexception
9162      * Fires when loading fails.
9163      * @param {Roo.JsonView} this
9164      * @param {Object} response The raw Connect response object
9165      */
9166     this.addEvents({
9167         'beforerender' : true,
9168         'load' : true,
9169         'loadexception' : true
9170     });
9171 };
9172 Roo.extend(Roo.JsonView, Roo.View, {
9173     /**
9174      * @type {String} The root property in the loaded JSON object that contains the data
9175      */
9176     jsonRoot : "",
9177
9178     /**
9179      * Refreshes the view.
9180      */
9181     refresh : function(){
9182         this.clearSelections();
9183         this.el.update("");
9184         var html = [];
9185         var o = this.jsonData;
9186         if(o && o.length > 0){
9187             for(var i = 0, len = o.length; i < len; i++){
9188                 var data = this.prepareData(o[i], i, o);
9189                 html[html.length] = this.tpl.apply(data);
9190             }
9191         }else{
9192             html.push(this.emptyText);
9193         }
9194         this.el.update(html.join(""));
9195         this.nodes = this.el.dom.childNodes;
9196         this.updateIndexes(0);
9197     },
9198
9199     /**
9200      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9201      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9202      <pre><code>
9203      view.load({
9204          url: "your-url.php",
9205          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9206          callback: yourFunction,
9207          scope: yourObject, //(optional scope)
9208          discardUrl: false,
9209          nocache: false,
9210          text: "Loading...",
9211          timeout: 30,
9212          scripts: false
9213      });
9214      </code></pre>
9215      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9216      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9217      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9218      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9219      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9220      */
9221     load : function(){
9222         var um = this.el.getUpdateManager();
9223         um.update.apply(um, arguments);
9224     },
9225
9226     render : function(el, response){
9227         this.clearSelections();
9228         this.el.update("");
9229         var o;
9230         try{
9231             o = Roo.util.JSON.decode(response.responseText);
9232             if(this.jsonRoot){
9233                 
9234                 o = o[this.jsonRoot];
9235             }
9236         } catch(e){
9237         }
9238         /**
9239          * The current JSON data or null
9240          */
9241         this.jsonData = o;
9242         this.beforeRender();
9243         this.refresh();
9244     },
9245
9246 /**
9247  * Get the number of records in the current JSON dataset
9248  * @return {Number}
9249  */
9250     getCount : function(){
9251         return this.jsonData ? this.jsonData.length : 0;
9252     },
9253
9254 /**
9255  * Returns the JSON object for the specified node(s)
9256  * @param {HTMLElement/Array} node The node or an array of nodes
9257  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9258  * you get the JSON object for the node
9259  */
9260     getNodeData : function(node){
9261         if(node instanceof Array){
9262             var data = [];
9263             for(var i = 0, len = node.length; i < len; i++){
9264                 data.push(this.getNodeData(node[i]));
9265             }
9266             return data;
9267         }
9268         return this.jsonData[this.indexOf(node)] || null;
9269     },
9270
9271     beforeRender : function(){
9272         this.snapshot = this.jsonData;
9273         if(this.sortInfo){
9274             this.sort.apply(this, this.sortInfo);
9275         }
9276         this.fireEvent("beforerender", this, this.jsonData);
9277     },
9278
9279     onLoad : function(el, o){
9280         this.fireEvent("load", this, this.jsonData, o);
9281     },
9282
9283     onLoadException : function(el, o){
9284         this.fireEvent("loadexception", this, o);
9285     },
9286
9287 /**
9288  * Filter the data by a specific property.
9289  * @param {String} property A property on your JSON objects
9290  * @param {String/RegExp} value Either string that the property values
9291  * should start with, or a RegExp to test against the property
9292  */
9293     filter : function(property, value){
9294         if(this.jsonData){
9295             var data = [];
9296             var ss = this.snapshot;
9297             if(typeof value == "string"){
9298                 var vlen = value.length;
9299                 if(vlen == 0){
9300                     this.clearFilter();
9301                     return;
9302                 }
9303                 value = value.toLowerCase();
9304                 for(var i = 0, len = ss.length; i < len; i++){
9305                     var o = ss[i];
9306                     if(o[property].substr(0, vlen).toLowerCase() == value){
9307                         data.push(o);
9308                     }
9309                 }
9310             } else if(value.exec){ // regex?
9311                 for(var i = 0, len = ss.length; i < len; i++){
9312                     var o = ss[i];
9313                     if(value.test(o[property])){
9314                         data.push(o);
9315                     }
9316                 }
9317             } else{
9318                 return;
9319             }
9320             this.jsonData = data;
9321             this.refresh();
9322         }
9323     },
9324
9325 /**
9326  * Filter by a function. The passed function will be called with each
9327  * object in the current dataset. If the function returns true the value is kept,
9328  * otherwise it is filtered.
9329  * @param {Function} fn
9330  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9331  */
9332     filterBy : function(fn, scope){
9333         if(this.jsonData){
9334             var data = [];
9335             var ss = this.snapshot;
9336             for(var i = 0, len = ss.length; i < len; i++){
9337                 var o = ss[i];
9338                 if(fn.call(scope || this, o)){
9339                     data.push(o);
9340                 }
9341             }
9342             this.jsonData = data;
9343             this.refresh();
9344         }
9345     },
9346
9347 /**
9348  * Clears the current filter.
9349  */
9350     clearFilter : function(){
9351         if(this.snapshot && this.jsonData != this.snapshot){
9352             this.jsonData = this.snapshot;
9353             this.refresh();
9354         }
9355     },
9356
9357
9358 /**
9359  * Sorts the data for this view and refreshes it.
9360  * @param {String} property A property on your JSON objects to sort on
9361  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9362  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9363  */
9364     sort : function(property, dir, sortType){
9365         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9366         if(this.jsonData){
9367             var p = property;
9368             var dsc = dir && dir.toLowerCase() == "desc";
9369             var f = function(o1, o2){
9370                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9371                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9372                 ;
9373                 if(v1 < v2){
9374                     return dsc ? +1 : -1;
9375                 } else if(v1 > v2){
9376                     return dsc ? -1 : +1;
9377                 } else{
9378                     return 0;
9379                 }
9380             };
9381             this.jsonData.sort(f);
9382             this.refresh();
9383             if(this.jsonData != this.snapshot){
9384                 this.snapshot.sort(f);
9385             }
9386         }
9387     }
9388 });/*
9389  * Based on:
9390  * Ext JS Library 1.1.1
9391  * Copyright(c) 2006-2007, Ext JS, LLC.
9392  *
9393  * Originally Released Under LGPL - original licence link has changed is not relivant.
9394  *
9395  * Fork - LGPL
9396  * <script type="text/javascript">
9397  */
9398  
9399
9400 /**
9401  * @class Roo.ColorPalette
9402  * @extends Roo.Component
9403  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9404  * Here's an example of typical usage:
9405  * <pre><code>
9406 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9407 cp.render('my-div');
9408
9409 cp.on('select', function(palette, selColor){
9410     // do something with selColor
9411 });
9412 </code></pre>
9413  * @constructor
9414  * Create a new ColorPalette
9415  * @param {Object} config The config object
9416  */
9417 Roo.ColorPalette = function(config){
9418     Roo.ColorPalette.superclass.constructor.call(this, config);
9419     this.addEvents({
9420         /**
9421              * @event select
9422              * Fires when a color is selected
9423              * @param {ColorPalette} this
9424              * @param {String} color The 6-digit color hex code (without the # symbol)
9425              */
9426         select: true
9427     });
9428
9429     if(this.handler){
9430         this.on("select", this.handler, this.scope, true);
9431     }
9432 };
9433 Roo.extend(Roo.ColorPalette, Roo.Component, {
9434     /**
9435      * @cfg {String} itemCls
9436      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9437      */
9438     itemCls : "x-color-palette",
9439     /**
9440      * @cfg {String} value
9441      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9442      * the hex codes are case-sensitive.
9443      */
9444     value : null,
9445     clickEvent:'click',
9446     // private
9447     ctype: "Roo.ColorPalette",
9448
9449     /**
9450      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9451      */
9452     allowReselect : false,
9453
9454     /**
9455      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9456      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9457      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9458      * of colors with the width setting until the box is symmetrical.</p>
9459      * <p>You can override individual colors if needed:</p>
9460      * <pre><code>
9461 var cp = new Roo.ColorPalette();
9462 cp.colors[0] = "FF0000";  // change the first box to red
9463 </code></pre>
9464
9465 Or you can provide a custom array of your own for complete control:
9466 <pre><code>
9467 var cp = new Roo.ColorPalette();
9468 cp.colors = ["000000", "993300", "333300"];
9469 </code></pre>
9470      * @type Array
9471      */
9472     colors : [
9473         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9474         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9475         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9476         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9477         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9478     ],
9479
9480     // private
9481     onRender : function(container, position){
9482         var t = new Roo.MasterTemplate(
9483             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9484         );
9485         var c = this.colors;
9486         for(var i = 0, len = c.length; i < len; i++){
9487             t.add([c[i]]);
9488         }
9489         var el = document.createElement("div");
9490         el.className = this.itemCls;
9491         t.overwrite(el);
9492         container.dom.insertBefore(el, position);
9493         this.el = Roo.get(el);
9494         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9495         if(this.clickEvent != 'click'){
9496             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9497         }
9498     },
9499
9500     // private
9501     afterRender : function(){
9502         Roo.ColorPalette.superclass.afterRender.call(this);
9503         if(this.value){
9504             var s = this.value;
9505             this.value = null;
9506             this.select(s);
9507         }
9508     },
9509
9510     // private
9511     handleClick : function(e, t){
9512         e.preventDefault();
9513         if(!this.disabled){
9514             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9515             this.select(c.toUpperCase());
9516         }
9517     },
9518
9519     /**
9520      * Selects the specified color in the palette (fires the select event)
9521      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9522      */
9523     select : function(color){
9524         color = color.replace("#", "");
9525         if(color != this.value || this.allowReselect){
9526             var el = this.el;
9527             if(this.value){
9528                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9529             }
9530             el.child("a.color-"+color).addClass("x-color-palette-sel");
9531             this.value = color;
9532             this.fireEvent("select", this, color);
9533         }
9534     }
9535 });/*
9536  * Based on:
9537  * Ext JS Library 1.1.1
9538  * Copyright(c) 2006-2007, Ext JS, LLC.
9539  *
9540  * Originally Released Under LGPL - original licence link has changed is not relivant.
9541  *
9542  * Fork - LGPL
9543  * <script type="text/javascript">
9544  */
9545  
9546 /**
9547  * @class Roo.DatePicker
9548  * @extends Roo.Component
9549  * Simple date picker class.
9550  * @constructor
9551  * Create a new DatePicker
9552  * @param {Object} config The config object
9553  */
9554 Roo.DatePicker = function(config){
9555     Roo.DatePicker.superclass.constructor.call(this, config);
9556
9557     this.value = config && config.value ?
9558                  config.value.clearTime() : new Date().clearTime();
9559
9560     this.addEvents({
9561         /**
9562              * @event select
9563              * Fires when a date is selected
9564              * @param {DatePicker} this
9565              * @param {Date} date The selected date
9566              */
9567         'select': true,
9568         /**
9569              * @event monthchange
9570              * Fires when the displayed month changes 
9571              * @param {DatePicker} this
9572              * @param {Date} date The selected month
9573              */
9574         'monthchange': true
9575     });
9576
9577     if(this.handler){
9578         this.on("select", this.handler,  this.scope || this);
9579     }
9580     // build the disabledDatesRE
9581     if(!this.disabledDatesRE && this.disabledDates){
9582         var dd = this.disabledDates;
9583         var re = "(?:";
9584         for(var i = 0; i < dd.length; i++){
9585             re += dd[i];
9586             if(i != dd.length-1) re += "|";
9587         }
9588         this.disabledDatesRE = new RegExp(re + ")");
9589     }
9590 };
9591
9592 Roo.extend(Roo.DatePicker, Roo.Component, {
9593     /**
9594      * @cfg {String} todayText
9595      * The text to display on the button that selects the current date (defaults to "Today")
9596      */
9597     todayText : "Today",
9598     /**
9599      * @cfg {String} okText
9600      * The text to display on the ok button
9601      */
9602     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9603     /**
9604      * @cfg {String} cancelText
9605      * The text to display on the cancel button
9606      */
9607     cancelText : "Cancel",
9608     /**
9609      * @cfg {String} todayTip
9610      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9611      */
9612     todayTip : "{0} (Spacebar)",
9613     /**
9614      * @cfg {Date} minDate
9615      * Minimum allowable date (JavaScript date object, defaults to null)
9616      */
9617     minDate : null,
9618     /**
9619      * @cfg {Date} maxDate
9620      * Maximum allowable date (JavaScript date object, defaults to null)
9621      */
9622     maxDate : null,
9623     /**
9624      * @cfg {String} minText
9625      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9626      */
9627     minText : "This date is before the minimum date",
9628     /**
9629      * @cfg {String} maxText
9630      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9631      */
9632     maxText : "This date is after the maximum date",
9633     /**
9634      * @cfg {String} format
9635      * The default date format string which can be overriden for localization support.  The format must be
9636      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9637      */
9638     format : "m/d/y",
9639     /**
9640      * @cfg {Array} disabledDays
9641      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9642      */
9643     disabledDays : null,
9644     /**
9645      * @cfg {String} disabledDaysText
9646      * The tooltip to display when the date falls on a disabled day (defaults to "")
9647      */
9648     disabledDaysText : "",
9649     /**
9650      * @cfg {RegExp} disabledDatesRE
9651      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9652      */
9653     disabledDatesRE : null,
9654     /**
9655      * @cfg {String} disabledDatesText
9656      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9657      */
9658     disabledDatesText : "",
9659     /**
9660      * @cfg {Boolean} constrainToViewport
9661      * True to constrain the date picker to the viewport (defaults to true)
9662      */
9663     constrainToViewport : true,
9664     /**
9665      * @cfg {Array} monthNames
9666      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9667      */
9668     monthNames : Date.monthNames,
9669     /**
9670      * @cfg {Array} dayNames
9671      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9672      */
9673     dayNames : Date.dayNames,
9674     /**
9675      * @cfg {String} nextText
9676      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9677      */
9678     nextText: 'Next Month (Control+Right)',
9679     /**
9680      * @cfg {String} prevText
9681      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9682      */
9683     prevText: 'Previous Month (Control+Left)',
9684     /**
9685      * @cfg {String} monthYearText
9686      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9687      */
9688     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9689     /**
9690      * @cfg {Number} startDay
9691      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9692      */
9693     startDay : 0,
9694     /**
9695      * @cfg {Bool} showClear
9696      * Show a clear button (usefull for date form elements that can be blank.)
9697      */
9698     
9699     showClear: false,
9700     
9701     /**
9702      * Sets the value of the date field
9703      * @param {Date} value The date to set
9704      */
9705     setValue : function(value){
9706         var old = this.value;
9707         
9708         if (typeof(value) == 'string') {
9709          
9710             value = Date.parseDate(value, this.format);
9711         }
9712         if (!value) {
9713             value = new Date();
9714         }
9715         
9716         this.value = value.clearTime(true);
9717         if(this.el){
9718             this.update(this.value);
9719         }
9720     },
9721
9722     /**
9723      * Gets the current selected value of the date field
9724      * @return {Date} The selected date
9725      */
9726     getValue : function(){
9727         return this.value;
9728     },
9729
9730     // private
9731     focus : function(){
9732         if(this.el){
9733             this.update(this.activeDate);
9734         }
9735     },
9736
9737     // privateval
9738     onRender : function(container, position){
9739         
9740         var m = [
9741              '<table cellspacing="0">',
9742                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
9743                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9744         var dn = this.dayNames;
9745         for(var i = 0; i < 7; i++){
9746             var d = this.startDay+i;
9747             if(d > 6){
9748                 d = d-7;
9749             }
9750             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9751         }
9752         m[m.length] = "</tr></thead><tbody><tr>";
9753         for(var i = 0; i < 42; i++) {
9754             if(i % 7 == 0 && i != 0){
9755                 m[m.length] = "</tr><tr>";
9756             }
9757             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9758         }
9759         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9760             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9761
9762         var el = document.createElement("div");
9763         el.className = "x-date-picker";
9764         el.innerHTML = m.join("");
9765
9766         container.dom.insertBefore(el, position);
9767
9768         this.el = Roo.get(el);
9769         this.eventEl = Roo.get(el.firstChild);
9770
9771         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9772             handler: this.showPrevMonth,
9773             scope: this,
9774             preventDefault:true,
9775             stopDefault:true
9776         });
9777
9778         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9779             handler: this.showNextMonth,
9780             scope: this,
9781             preventDefault:true,
9782             stopDefault:true
9783         });
9784
9785         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9786
9787         this.monthPicker = this.el.down('div.x-date-mp');
9788         this.monthPicker.enableDisplayMode('block');
9789         
9790         var kn = new Roo.KeyNav(this.eventEl, {
9791             "left" : function(e){
9792                 e.ctrlKey ?
9793                     this.showPrevMonth() :
9794                     this.update(this.activeDate.add("d", -1));
9795             },
9796
9797             "right" : function(e){
9798                 e.ctrlKey ?
9799                     this.showNextMonth() :
9800                     this.update(this.activeDate.add("d", 1));
9801             },
9802
9803             "up" : function(e){
9804                 e.ctrlKey ?
9805                     this.showNextYear() :
9806                     this.update(this.activeDate.add("d", -7));
9807             },
9808
9809             "down" : function(e){
9810                 e.ctrlKey ?
9811                     this.showPrevYear() :
9812                     this.update(this.activeDate.add("d", 7));
9813             },
9814
9815             "pageUp" : function(e){
9816                 this.showNextMonth();
9817             },
9818
9819             "pageDown" : function(e){
9820                 this.showPrevMonth();
9821             },
9822
9823             "enter" : function(e){
9824                 e.stopPropagation();
9825                 return true;
9826             },
9827
9828             scope : this
9829         });
9830
9831         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9832
9833         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9834
9835         this.el.unselectable();
9836         
9837         this.cells = this.el.select("table.x-date-inner tbody td");
9838         this.textNodes = this.el.query("table.x-date-inner tbody span");
9839
9840         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9841             text: "&#160;",
9842             tooltip: this.monthYearText
9843         });
9844
9845         this.mbtn.on('click', this.showMonthPicker, this);
9846         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9847
9848
9849         var today = (new Date()).dateFormat(this.format);
9850         
9851         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9852         if (this.showClear) {
9853             baseTb.add( new Roo.Toolbar.Fill());
9854         }
9855         baseTb.add({
9856             text: String.format(this.todayText, today),
9857             tooltip: String.format(this.todayTip, today),
9858             handler: this.selectToday,
9859             scope: this
9860         });
9861         
9862         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9863             
9864         //});
9865         if (this.showClear) {
9866             
9867             baseTb.add( new Roo.Toolbar.Fill());
9868             baseTb.add({
9869                 text: '&#160;',
9870                 cls: 'x-btn-icon x-btn-clear',
9871                 handler: function() {
9872                     //this.value = '';
9873                     this.fireEvent("select", this, '');
9874                 },
9875                 scope: this
9876             });
9877         }
9878         
9879         
9880         if(Roo.isIE){
9881             this.el.repaint();
9882         }
9883         this.update(this.value);
9884     },
9885
9886     createMonthPicker : function(){
9887         if(!this.monthPicker.dom.firstChild){
9888             var buf = ['<table border="0" cellspacing="0">'];
9889             for(var i = 0; i < 6; i++){
9890                 buf.push(
9891                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9892                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9893                     i == 0 ?
9894                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
9895                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9896                 );
9897             }
9898             buf.push(
9899                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9900                     this.okText,
9901                     '</button><button type="button" class="x-date-mp-cancel">',
9902                     this.cancelText,
9903                     '</button></td></tr>',
9904                 '</table>'
9905             );
9906             this.monthPicker.update(buf.join(''));
9907             this.monthPicker.on('click', this.onMonthClick, this);
9908             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9909
9910             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9911             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9912
9913             this.mpMonths.each(function(m, a, i){
9914                 i += 1;
9915                 if((i%2) == 0){
9916                     m.dom.xmonth = 5 + Math.round(i * .5);
9917                 }else{
9918                     m.dom.xmonth = Math.round((i-1) * .5);
9919                 }
9920             });
9921         }
9922     },
9923
9924     showMonthPicker : function(){
9925         this.createMonthPicker();
9926         var size = this.el.getSize();
9927         this.monthPicker.setSize(size);
9928         this.monthPicker.child('table').setSize(size);
9929
9930         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9931         this.updateMPMonth(this.mpSelMonth);
9932         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9933         this.updateMPYear(this.mpSelYear);
9934
9935         this.monthPicker.slideIn('t', {duration:.2});
9936     },
9937
9938     updateMPYear : function(y){
9939         this.mpyear = y;
9940         var ys = this.mpYears.elements;
9941         for(var i = 1; i <= 10; i++){
9942             var td = ys[i-1], y2;
9943             if((i%2) == 0){
9944                 y2 = y + Math.round(i * .5);
9945                 td.firstChild.innerHTML = y2;
9946                 td.xyear = y2;
9947             }else{
9948                 y2 = y - (5-Math.round(i * .5));
9949                 td.firstChild.innerHTML = y2;
9950                 td.xyear = y2;
9951             }
9952             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9953         }
9954     },
9955
9956     updateMPMonth : function(sm){
9957         this.mpMonths.each(function(m, a, i){
9958             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9959         });
9960     },
9961
9962     selectMPMonth: function(m){
9963         
9964     },
9965
9966     onMonthClick : function(e, t){
9967         e.stopEvent();
9968         var el = new Roo.Element(t), pn;
9969         if(el.is('button.x-date-mp-cancel')){
9970             this.hideMonthPicker();
9971         }
9972         else if(el.is('button.x-date-mp-ok')){
9973             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9974             this.hideMonthPicker();
9975         }
9976         else if(pn = el.up('td.x-date-mp-month', 2)){
9977             this.mpMonths.removeClass('x-date-mp-sel');
9978             pn.addClass('x-date-mp-sel');
9979             this.mpSelMonth = pn.dom.xmonth;
9980         }
9981         else if(pn = el.up('td.x-date-mp-year', 2)){
9982             this.mpYears.removeClass('x-date-mp-sel');
9983             pn.addClass('x-date-mp-sel');
9984             this.mpSelYear = pn.dom.xyear;
9985         }
9986         else if(el.is('a.x-date-mp-prev')){
9987             this.updateMPYear(this.mpyear-10);
9988         }
9989         else if(el.is('a.x-date-mp-next')){
9990             this.updateMPYear(this.mpyear+10);
9991         }
9992     },
9993
9994     onMonthDblClick : function(e, t){
9995         e.stopEvent();
9996         var el = new Roo.Element(t), pn;
9997         if(pn = el.up('td.x-date-mp-month', 2)){
9998             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
9999             this.hideMonthPicker();
10000         }
10001         else if(pn = el.up('td.x-date-mp-year', 2)){
10002             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10003             this.hideMonthPicker();
10004         }
10005     },
10006
10007     hideMonthPicker : function(disableAnim){
10008         if(this.monthPicker){
10009             if(disableAnim === true){
10010                 this.monthPicker.hide();
10011             }else{
10012                 this.monthPicker.slideOut('t', {duration:.2});
10013             }
10014         }
10015     },
10016
10017     // private
10018     showPrevMonth : function(e){
10019         this.update(this.activeDate.add("mo", -1));
10020     },
10021
10022     // private
10023     showNextMonth : function(e){
10024         this.update(this.activeDate.add("mo", 1));
10025     },
10026
10027     // private
10028     showPrevYear : function(){
10029         this.update(this.activeDate.add("y", -1));
10030     },
10031
10032     // private
10033     showNextYear : function(){
10034         this.update(this.activeDate.add("y", 1));
10035     },
10036
10037     // private
10038     handleMouseWheel : function(e){
10039         var delta = e.getWheelDelta();
10040         if(delta > 0){
10041             this.showPrevMonth();
10042             e.stopEvent();
10043         } else if(delta < 0){
10044             this.showNextMonth();
10045             e.stopEvent();
10046         }
10047     },
10048
10049     // private
10050     handleDateClick : function(e, t){
10051         e.stopEvent();
10052         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10053             this.setValue(new Date(t.dateValue));
10054             this.fireEvent("select", this, this.value);
10055         }
10056     },
10057
10058     // private
10059     selectToday : function(){
10060         this.setValue(new Date().clearTime());
10061         this.fireEvent("select", this, this.value);
10062     },
10063
10064     // private
10065     update : function(date)
10066     {
10067         var vd = this.activeDate;
10068         this.activeDate = date;
10069         if(vd && this.el){
10070             var t = date.getTime();
10071             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10072                 this.cells.removeClass("x-date-selected");
10073                 this.cells.each(function(c){
10074                    if(c.dom.firstChild.dateValue == t){
10075                        c.addClass("x-date-selected");
10076                        setTimeout(function(){
10077                             try{c.dom.firstChild.focus();}catch(e){}
10078                        }, 50);
10079                        return false;
10080                    }
10081                 });
10082                 return;
10083             }
10084         }
10085         
10086         var days = date.getDaysInMonth();
10087         var firstOfMonth = date.getFirstDateOfMonth();
10088         var startingPos = firstOfMonth.getDay()-this.startDay;
10089
10090         if(startingPos <= this.startDay){
10091             startingPos += 7;
10092         }
10093
10094         var pm = date.add("mo", -1);
10095         var prevStart = pm.getDaysInMonth()-startingPos;
10096
10097         var cells = this.cells.elements;
10098         var textEls = this.textNodes;
10099         days += startingPos;
10100
10101         // convert everything to numbers so it's fast
10102         var day = 86400000;
10103         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10104         var today = new Date().clearTime().getTime();
10105         var sel = date.clearTime().getTime();
10106         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10107         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10108         var ddMatch = this.disabledDatesRE;
10109         var ddText = this.disabledDatesText;
10110         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10111         var ddaysText = this.disabledDaysText;
10112         var format = this.format;
10113
10114         var setCellClass = function(cal, cell){
10115             cell.title = "";
10116             var t = d.getTime();
10117             cell.firstChild.dateValue = t;
10118             if(t == today){
10119                 cell.className += " x-date-today";
10120                 cell.title = cal.todayText;
10121             }
10122             if(t == sel){
10123                 cell.className += " x-date-selected";
10124                 setTimeout(function(){
10125                     try{cell.firstChild.focus();}catch(e){}
10126                 }, 50);
10127             }
10128             // disabling
10129             if(t < min) {
10130                 cell.className = " x-date-disabled";
10131                 cell.title = cal.minText;
10132                 return;
10133             }
10134             if(t > max) {
10135                 cell.className = " x-date-disabled";
10136                 cell.title = cal.maxText;
10137                 return;
10138             }
10139             if(ddays){
10140                 if(ddays.indexOf(d.getDay()) != -1){
10141                     cell.title = ddaysText;
10142                     cell.className = " x-date-disabled";
10143                 }
10144             }
10145             if(ddMatch && format){
10146                 var fvalue = d.dateFormat(format);
10147                 if(ddMatch.test(fvalue)){
10148                     cell.title = ddText.replace("%0", fvalue);
10149                     cell.className = " x-date-disabled";
10150                 }
10151             }
10152         };
10153
10154         var i = 0;
10155         for(; i < startingPos; i++) {
10156             textEls[i].innerHTML = (++prevStart);
10157             d.setDate(d.getDate()+1);
10158             cells[i].className = "x-date-prevday";
10159             setCellClass(this, cells[i]);
10160         }
10161         for(; i < days; i++){
10162             intDay = i - startingPos + 1;
10163             textEls[i].innerHTML = (intDay);
10164             d.setDate(d.getDate()+1);
10165             cells[i].className = "x-date-active";
10166             setCellClass(this, cells[i]);
10167         }
10168         var extraDays = 0;
10169         for(; i < 42; i++) {
10170              textEls[i].innerHTML = (++extraDays);
10171              d.setDate(d.getDate()+1);
10172              cells[i].className = "x-date-nextday";
10173              setCellClass(this, cells[i]);
10174         }
10175
10176         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10177         this.fireEvent('monthchange', this, date);
10178         
10179         if(!this.internalRender){
10180             var main = this.el.dom.firstChild;
10181             var w = main.offsetWidth;
10182             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10183             Roo.fly(main).setWidth(w);
10184             this.internalRender = true;
10185             // opera does not respect the auto grow header center column
10186             // then, after it gets a width opera refuses to recalculate
10187             // without a second pass
10188             if(Roo.isOpera && !this.secondPass){
10189                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10190                 this.secondPass = true;
10191                 this.update.defer(10, this, [date]);
10192             }
10193         }
10194         
10195         
10196     }
10197 });        /*
10198  * Based on:
10199  * Ext JS Library 1.1.1
10200  * Copyright(c) 2006-2007, Ext JS, LLC.
10201  *
10202  * Originally Released Under LGPL - original licence link has changed is not relivant.
10203  *
10204  * Fork - LGPL
10205  * <script type="text/javascript">
10206  */
10207 /**
10208  * @class Roo.TabPanel
10209  * @extends Roo.util.Observable
10210  * A lightweight tab container.
10211  * <br><br>
10212  * Usage:
10213  * <pre><code>
10214 // basic tabs 1, built from existing content
10215 var tabs = new Roo.TabPanel("tabs1");
10216 tabs.addTab("script", "View Script");
10217 tabs.addTab("markup", "View Markup");
10218 tabs.activate("script");
10219
10220 // more advanced tabs, built from javascript
10221 var jtabs = new Roo.TabPanel("jtabs");
10222 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10223
10224 // set up the UpdateManager
10225 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10226 var updater = tab2.getUpdateManager();
10227 updater.setDefaultUrl("ajax1.htm");
10228 tab2.on('activate', updater.refresh, updater, true);
10229
10230 // Use setUrl for Ajax loading
10231 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10232 tab3.setUrl("ajax2.htm", null, true);
10233
10234 // Disabled tab
10235 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10236 tab4.disable();
10237
10238 jtabs.activate("jtabs-1");
10239  * </code></pre>
10240  * @constructor
10241  * Create a new TabPanel.
10242  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10243  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10244  */
10245 Roo.TabPanel = function(container, config){
10246     /**
10247     * The container element for this TabPanel.
10248     * @type Roo.Element
10249     */
10250     this.el = Roo.get(container, true);
10251     if(config){
10252         if(typeof config == "boolean"){
10253             this.tabPosition = config ? "bottom" : "top";
10254         }else{
10255             Roo.apply(this, config);
10256         }
10257     }
10258     if(this.tabPosition == "bottom"){
10259         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10260         this.el.addClass("x-tabs-bottom");
10261     }
10262     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10263     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10264     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10265     if(Roo.isIE){
10266         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10267     }
10268     if(this.tabPosition != "bottom"){
10269         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10270          * @type Roo.Element
10271          */
10272         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10273         this.el.addClass("x-tabs-top");
10274     }
10275     this.items = [];
10276
10277     this.bodyEl.setStyle("position", "relative");
10278
10279     this.active = null;
10280     this.activateDelegate = this.activate.createDelegate(this);
10281
10282     this.addEvents({
10283         /**
10284          * @event tabchange
10285          * Fires when the active tab changes
10286          * @param {Roo.TabPanel} this
10287          * @param {Roo.TabPanelItem} activePanel The new active tab
10288          */
10289         "tabchange": true,
10290         /**
10291          * @event beforetabchange
10292          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10293          * @param {Roo.TabPanel} this
10294          * @param {Object} e Set cancel to true on this object to cancel the tab change
10295          * @param {Roo.TabPanelItem} tab The tab being changed to
10296          */
10297         "beforetabchange" : true
10298     });
10299
10300     Roo.EventManager.onWindowResize(this.onResize, this);
10301     this.cpad = this.el.getPadding("lr");
10302     this.hiddenCount = 0;
10303
10304
10305     // toolbar on the tabbar support...
10306     if (this.toolbar) {
10307         var tcfg = this.toolbar;
10308         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10309         this.toolbar = new Roo.Toolbar(tcfg);
10310         if (Roo.isSafari) {
10311             var tbl = tcfg.container.child('table', true);
10312             tbl.setAttribute('width', '100%');
10313         }
10314         
10315     }
10316    
10317
10318
10319     Roo.TabPanel.superclass.constructor.call(this);
10320 };
10321
10322 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10323     /*
10324      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10325      */
10326     tabPosition : "top",
10327     /*
10328      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10329      */
10330     currentTabWidth : 0,
10331     /*
10332      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10333      */
10334     minTabWidth : 40,
10335     /*
10336      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10337      */
10338     maxTabWidth : 250,
10339     /*
10340      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10341      */
10342     preferredTabWidth : 175,
10343     /*
10344      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10345      */
10346     resizeTabs : false,
10347     /*
10348      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10349      */
10350     monitorResize : true,
10351     /*
10352      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10353      */
10354     toolbar : false,
10355
10356     /**
10357      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10358      * @param {String} id The id of the div to use <b>or create</b>
10359      * @param {String} text The text for the tab
10360      * @param {String} content (optional) Content to put in the TabPanelItem body
10361      * @param {Boolean} closable (optional) True to create a close icon on the tab
10362      * @return {Roo.TabPanelItem} The created TabPanelItem
10363      */
10364     addTab : function(id, text, content, closable){
10365         var item = new Roo.TabPanelItem(this, id, text, closable);
10366         this.addTabItem(item);
10367         if(content){
10368             item.setContent(content);
10369         }
10370         return item;
10371     },
10372
10373     /**
10374      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10375      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10376      * @return {Roo.TabPanelItem}
10377      */
10378     getTab : function(id){
10379         return this.items[id];
10380     },
10381
10382     /**
10383      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10384      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10385      */
10386     hideTab : function(id){
10387         var t = this.items[id];
10388         if(!t.isHidden()){
10389            t.setHidden(true);
10390            this.hiddenCount++;
10391            this.autoSizeTabs();
10392         }
10393     },
10394
10395     /**
10396      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10397      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10398      */
10399     unhideTab : function(id){
10400         var t = this.items[id];
10401         if(t.isHidden()){
10402            t.setHidden(false);
10403            this.hiddenCount--;
10404            this.autoSizeTabs();
10405         }
10406     },
10407
10408     /**
10409      * Adds an existing {@link Roo.TabPanelItem}.
10410      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10411      */
10412     addTabItem : function(item){
10413         this.items[item.id] = item;
10414         this.items.push(item);
10415         if(this.resizeTabs){
10416            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10417            this.autoSizeTabs();
10418         }else{
10419             item.autoSize();
10420         }
10421     },
10422
10423     /**
10424      * Removes a {@link Roo.TabPanelItem}.
10425      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10426      */
10427     removeTab : function(id){
10428         var items = this.items;
10429         var tab = items[id];
10430         if(!tab) { return; }
10431         var index = items.indexOf(tab);
10432         if(this.active == tab && items.length > 1){
10433             var newTab = this.getNextAvailable(index);
10434             if(newTab) {
10435                 newTab.activate();
10436             }
10437         }
10438         this.stripEl.dom.removeChild(tab.pnode.dom);
10439         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10440             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10441         }
10442         items.splice(index, 1);
10443         delete this.items[tab.id];
10444         tab.fireEvent("close", tab);
10445         tab.purgeListeners();
10446         this.autoSizeTabs();
10447     },
10448
10449     getNextAvailable : function(start){
10450         var items = this.items;
10451         var index = start;
10452         // look for a next tab that will slide over to
10453         // replace the one being removed
10454         while(index < items.length){
10455             var item = items[++index];
10456             if(item && !item.isHidden()){
10457                 return item;
10458             }
10459         }
10460         // if one isn't found select the previous tab (on the left)
10461         index = start;
10462         while(index >= 0){
10463             var item = items[--index];
10464             if(item && !item.isHidden()){
10465                 return item;
10466             }
10467         }
10468         return null;
10469     },
10470
10471     /**
10472      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10473      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10474      */
10475     disableTab : function(id){
10476         var tab = this.items[id];
10477         if(tab && this.active != tab){
10478             tab.disable();
10479         }
10480     },
10481
10482     /**
10483      * Enables a {@link Roo.TabPanelItem} that is disabled.
10484      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10485      */
10486     enableTab : function(id){
10487         var tab = this.items[id];
10488         tab.enable();
10489     },
10490
10491     /**
10492      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10493      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10494      * @return {Roo.TabPanelItem} The TabPanelItem.
10495      */
10496     activate : function(id){
10497         var tab = this.items[id];
10498         if(!tab){
10499             return null;
10500         }
10501         if(tab == this.active || tab.disabled){
10502             return tab;
10503         }
10504         var e = {};
10505         this.fireEvent("beforetabchange", this, e, tab);
10506         if(e.cancel !== true && !tab.disabled){
10507             if(this.active){
10508                 this.active.hide();
10509             }
10510             this.active = this.items[id];
10511             this.active.show();
10512             this.fireEvent("tabchange", this, this.active);
10513         }
10514         return tab;
10515     },
10516
10517     /**
10518      * Gets the active {@link Roo.TabPanelItem}.
10519      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10520      */
10521     getActiveTab : function(){
10522         return this.active;
10523     },
10524
10525     /**
10526      * Updates the tab body element to fit the height of the container element
10527      * for overflow scrolling
10528      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10529      */
10530     syncHeight : function(targetHeight){
10531         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10532         var bm = this.bodyEl.getMargins();
10533         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10534         this.bodyEl.setHeight(newHeight);
10535         return newHeight;
10536     },
10537
10538     onResize : function(){
10539         if(this.monitorResize){
10540             this.autoSizeTabs();
10541         }
10542     },
10543
10544     /**
10545      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10546      */
10547     beginUpdate : function(){
10548         this.updating = true;
10549     },
10550
10551     /**
10552      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10553      */
10554     endUpdate : function(){
10555         this.updating = false;
10556         this.autoSizeTabs();
10557     },
10558
10559     /**
10560      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10561      */
10562     autoSizeTabs : function(){
10563         var count = this.items.length;
10564         var vcount = count - this.hiddenCount;
10565         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10566         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10567         var availWidth = Math.floor(w / vcount);
10568         var b = this.stripBody;
10569         if(b.getWidth() > w){
10570             var tabs = this.items;
10571             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10572             if(availWidth < this.minTabWidth){
10573                 /*if(!this.sleft){    // incomplete scrolling code
10574                     this.createScrollButtons();
10575                 }
10576                 this.showScroll();
10577                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10578             }
10579         }else{
10580             if(this.currentTabWidth < this.preferredTabWidth){
10581                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10582             }
10583         }
10584     },
10585
10586     /**
10587      * Returns the number of tabs in this TabPanel.
10588      * @return {Number}
10589      */
10590      getCount : function(){
10591          return this.items.length;
10592      },
10593
10594     /**
10595      * Resizes all the tabs to the passed width
10596      * @param {Number} The new width
10597      */
10598     setTabWidth : function(width){
10599         this.currentTabWidth = width;
10600         for(var i = 0, len = this.items.length; i < len; i++) {
10601                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10602         }
10603     },
10604
10605     /**
10606      * Destroys this TabPanel
10607      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10608      */
10609     destroy : function(removeEl){
10610         Roo.EventManager.removeResizeListener(this.onResize, this);
10611         for(var i = 0, len = this.items.length; i < len; i++){
10612             this.items[i].purgeListeners();
10613         }
10614         if(removeEl === true){
10615             this.el.update("");
10616             this.el.remove();
10617         }
10618     }
10619 });
10620
10621 /**
10622  * @class Roo.TabPanelItem
10623  * @extends Roo.util.Observable
10624  * Represents an individual item (tab plus body) in a TabPanel.
10625  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10626  * @param {String} id The id of this TabPanelItem
10627  * @param {String} text The text for the tab of this TabPanelItem
10628  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10629  */
10630 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10631     /**
10632      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10633      * @type Roo.TabPanel
10634      */
10635     this.tabPanel = tabPanel;
10636     /**
10637      * The id for this TabPanelItem
10638      * @type String
10639      */
10640     this.id = id;
10641     /** @private */
10642     this.disabled = false;
10643     /** @private */
10644     this.text = text;
10645     /** @private */
10646     this.loaded = false;
10647     this.closable = closable;
10648
10649     /**
10650      * The body element for this TabPanelItem.
10651      * @type Roo.Element
10652      */
10653     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10654     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10655     this.bodyEl.setStyle("display", "block");
10656     this.bodyEl.setStyle("zoom", "1");
10657     this.hideAction();
10658
10659     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10660     /** @private */
10661     this.el = Roo.get(els.el, true);
10662     this.inner = Roo.get(els.inner, true);
10663     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10664     this.pnode = Roo.get(els.el.parentNode, true);
10665     this.el.on("mousedown", this.onTabMouseDown, this);
10666     this.el.on("click", this.onTabClick, this);
10667     /** @private */
10668     if(closable){
10669         var c = Roo.get(els.close, true);
10670         c.dom.title = this.closeText;
10671         c.addClassOnOver("close-over");
10672         c.on("click", this.closeClick, this);
10673      }
10674
10675     this.addEvents({
10676          /**
10677          * @event activate
10678          * Fires when this tab becomes the active tab.
10679          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10680          * @param {Roo.TabPanelItem} this
10681          */
10682         "activate": true,
10683         /**
10684          * @event beforeclose
10685          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10686          * @param {Roo.TabPanelItem} this
10687          * @param {Object} e Set cancel to true on this object to cancel the close.
10688          */
10689         "beforeclose": true,
10690         /**
10691          * @event close
10692          * Fires when this tab is closed.
10693          * @param {Roo.TabPanelItem} this
10694          */
10695          "close": true,
10696         /**
10697          * @event deactivate
10698          * Fires when this tab is no longer the active tab.
10699          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10700          * @param {Roo.TabPanelItem} this
10701          */
10702          "deactivate" : true
10703     });
10704     this.hidden = false;
10705
10706     Roo.TabPanelItem.superclass.constructor.call(this);
10707 };
10708
10709 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10710     purgeListeners : function(){
10711        Roo.util.Observable.prototype.purgeListeners.call(this);
10712        this.el.removeAllListeners();
10713     },
10714     /**
10715      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10716      */
10717     show : function(){
10718         this.pnode.addClass("on");
10719         this.showAction();
10720         if(Roo.isOpera){
10721             this.tabPanel.stripWrap.repaint();
10722         }
10723         this.fireEvent("activate", this.tabPanel, this);
10724     },
10725
10726     /**
10727      * Returns true if this tab is the active tab.
10728      * @return {Boolean}
10729      */
10730     isActive : function(){
10731         return this.tabPanel.getActiveTab() == this;
10732     },
10733
10734     /**
10735      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10736      */
10737     hide : function(){
10738         this.pnode.removeClass("on");
10739         this.hideAction();
10740         this.fireEvent("deactivate", this.tabPanel, this);
10741     },
10742
10743     hideAction : function(){
10744         this.bodyEl.hide();
10745         this.bodyEl.setStyle("position", "absolute");
10746         this.bodyEl.setLeft("-20000px");
10747         this.bodyEl.setTop("-20000px");
10748     },
10749
10750     showAction : function(){
10751         this.bodyEl.setStyle("position", "relative");
10752         this.bodyEl.setTop("");
10753         this.bodyEl.setLeft("");
10754         this.bodyEl.show();
10755     },
10756
10757     /**
10758      * Set the tooltip for the tab.
10759      * @param {String} tooltip The tab's tooltip
10760      */
10761     setTooltip : function(text){
10762         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10763             this.textEl.dom.qtip = text;
10764             this.textEl.dom.removeAttribute('title');
10765         }else{
10766             this.textEl.dom.title = text;
10767         }
10768     },
10769
10770     onTabClick : function(e){
10771         e.preventDefault();
10772         this.tabPanel.activate(this.id);
10773     },
10774
10775     onTabMouseDown : function(e){
10776         e.preventDefault();
10777         this.tabPanel.activate(this.id);
10778     },
10779
10780     getWidth : function(){
10781         return this.inner.getWidth();
10782     },
10783
10784     setWidth : function(width){
10785         var iwidth = width - this.pnode.getPadding("lr");
10786         this.inner.setWidth(iwidth);
10787         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10788         this.pnode.setWidth(width);
10789     },
10790
10791     /**
10792      * Show or hide the tab
10793      * @param {Boolean} hidden True to hide or false to show.
10794      */
10795     setHidden : function(hidden){
10796         this.hidden = hidden;
10797         this.pnode.setStyle("display", hidden ? "none" : "");
10798     },
10799
10800     /**
10801      * Returns true if this tab is "hidden"
10802      * @return {Boolean}
10803      */
10804     isHidden : function(){
10805         return this.hidden;
10806     },
10807
10808     /**
10809      * Returns the text for this tab
10810      * @return {String}
10811      */
10812     getText : function(){
10813         return this.text;
10814     },
10815
10816     autoSize : function(){
10817         //this.el.beginMeasure();
10818         this.textEl.setWidth(1);
10819         /*
10820          *  #2804 [new] Tabs in Roojs
10821          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10822          */
10823         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10824         //this.el.endMeasure();
10825     },
10826
10827     /**
10828      * Sets the text for the tab (Note: this also sets the tooltip text)
10829      * @param {String} text The tab's text and tooltip
10830      */
10831     setText : function(text){
10832         this.text = text;
10833         this.textEl.update(text);
10834         this.setTooltip(text);
10835         if(!this.tabPanel.resizeTabs){
10836             this.autoSize();
10837         }
10838     },
10839     /**
10840      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10841      */
10842     activate : function(){
10843         this.tabPanel.activate(this.id);
10844     },
10845
10846     /**
10847      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10848      */
10849     disable : function(){
10850         if(this.tabPanel.active != this){
10851             this.disabled = true;
10852             this.pnode.addClass("disabled");
10853         }
10854     },
10855
10856     /**
10857      * Enables this TabPanelItem if it was previously disabled.
10858      */
10859     enable : function(){
10860         this.disabled = false;
10861         this.pnode.removeClass("disabled");
10862     },
10863
10864     /**
10865      * Sets the content for this TabPanelItem.
10866      * @param {String} content The content
10867      * @param {Boolean} loadScripts true to look for and load scripts
10868      */
10869     setContent : function(content, loadScripts){
10870         this.bodyEl.update(content, loadScripts);
10871     },
10872
10873     /**
10874      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10875      * @return {Roo.UpdateManager} The UpdateManager
10876      */
10877     getUpdateManager : function(){
10878         return this.bodyEl.getUpdateManager();
10879     },
10880
10881     /**
10882      * Set a URL to be used to load the content for this TabPanelItem.
10883      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10884      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
10885      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
10886      * @return {Roo.UpdateManager} The UpdateManager
10887      */
10888     setUrl : function(url, params, loadOnce){
10889         if(this.refreshDelegate){
10890             this.un('activate', this.refreshDelegate);
10891         }
10892         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10893         this.on("activate", this.refreshDelegate);
10894         return this.bodyEl.getUpdateManager();
10895     },
10896
10897     /** @private */
10898     _handleRefresh : function(url, params, loadOnce){
10899         if(!loadOnce || !this.loaded){
10900             var updater = this.bodyEl.getUpdateManager();
10901             updater.update(url, params, this._setLoaded.createDelegate(this));
10902         }
10903     },
10904
10905     /**
10906      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10907      *   Will fail silently if the setUrl method has not been called.
10908      *   This does not activate the panel, just updates its content.
10909      */
10910     refresh : function(){
10911         if(this.refreshDelegate){
10912            this.loaded = false;
10913            this.refreshDelegate();
10914         }
10915     },
10916
10917     /** @private */
10918     _setLoaded : function(){
10919         this.loaded = true;
10920     },
10921
10922     /** @private */
10923     closeClick : function(e){
10924         var o = {};
10925         e.stopEvent();
10926         this.fireEvent("beforeclose", this, o);
10927         if(o.cancel !== true){
10928             this.tabPanel.removeTab(this.id);
10929         }
10930     },
10931     /**
10932      * The text displayed in the tooltip for the close icon.
10933      * @type String
10934      */
10935     closeText : "Close this tab"
10936 });
10937
10938 /** @private */
10939 Roo.TabPanel.prototype.createStrip = function(container){
10940     var strip = document.createElement("div");
10941     strip.className = "x-tabs-wrap";
10942     container.appendChild(strip);
10943     return strip;
10944 };
10945 /** @private */
10946 Roo.TabPanel.prototype.createStripList = function(strip){
10947     // div wrapper for retard IE
10948     // returns the "tr" element.
10949     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10950         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10951         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10952     return strip.firstChild.firstChild.firstChild.firstChild;
10953 };
10954 /** @private */
10955 Roo.TabPanel.prototype.createBody = function(container){
10956     var body = document.createElement("div");
10957     Roo.id(body, "tab-body");
10958     Roo.fly(body).addClass("x-tabs-body");
10959     container.appendChild(body);
10960     return body;
10961 };
10962 /** @private */
10963 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10964     var body = Roo.getDom(id);
10965     if(!body){
10966         body = document.createElement("div");
10967         body.id = id;
10968     }
10969     Roo.fly(body).addClass("x-tabs-item-body");
10970     bodyEl.insertBefore(body, bodyEl.firstChild);
10971     return body;
10972 };
10973 /** @private */
10974 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10975     var td = document.createElement("td");
10976     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10977     //stripEl.appendChild(td);
10978     if(closable){
10979         td.className = "x-tabs-closable";
10980         if(!this.closeTpl){
10981             this.closeTpl = new Roo.Template(
10982                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10983                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10984                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10985             );
10986         }
10987         var el = this.closeTpl.overwrite(td, {"text": text});
10988         var close = el.getElementsByTagName("div")[0];
10989         var inner = el.getElementsByTagName("em")[0];
10990         return {"el": el, "close": close, "inner": inner};
10991     } else {
10992         if(!this.tabTpl){
10993             this.tabTpl = new Roo.Template(
10994                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10995                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
10996             );
10997         }
10998         var el = this.tabTpl.overwrite(td, {"text": text});
10999         var inner = el.getElementsByTagName("em")[0];
11000         return {"el": el, "inner": inner};
11001     }
11002 };/*
11003  * Based on:
11004  * Ext JS Library 1.1.1
11005  * Copyright(c) 2006-2007, Ext JS, LLC.
11006  *
11007  * Originally Released Under LGPL - original licence link has changed is not relivant.
11008  *
11009  * Fork - LGPL
11010  * <script type="text/javascript">
11011  */
11012
11013 /**
11014  * @class Roo.Button
11015  * @extends Roo.util.Observable
11016  * Simple Button class
11017  * @cfg {String} text The button text
11018  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11019  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11020  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11021  * @cfg {Object} scope The scope of the handler
11022  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11023  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11024  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11025  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11026  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11027  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11028    applies if enableToggle = true)
11029  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11030  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11031   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11032  * @constructor
11033  * Create a new button
11034  * @param {Object} config The config object
11035  */
11036 Roo.Button = function(renderTo, config)
11037 {
11038     if (!config) {
11039         config = renderTo;
11040         renderTo = config.renderTo || false;
11041     }
11042     
11043     Roo.apply(this, config);
11044     this.addEvents({
11045         /**
11046              * @event click
11047              * Fires when this button is clicked
11048              * @param {Button} this
11049              * @param {EventObject} e The click event
11050              */
11051             "click" : true,
11052         /**
11053              * @event toggle
11054              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11055              * @param {Button} this
11056              * @param {Boolean} pressed
11057              */
11058             "toggle" : true,
11059         /**
11060              * @event mouseover
11061              * Fires when the mouse hovers over the button
11062              * @param {Button} this
11063              * @param {Event} e The event object
11064              */
11065         'mouseover' : true,
11066         /**
11067              * @event mouseout
11068              * Fires when the mouse exits the button
11069              * @param {Button} this
11070              * @param {Event} e The event object
11071              */
11072         'mouseout': true,
11073          /**
11074              * @event render
11075              * Fires when the button is rendered
11076              * @param {Button} this
11077              */
11078         'render': true
11079     });
11080     if(this.menu){
11081         this.menu = Roo.menu.MenuMgr.get(this.menu);
11082     }
11083     // register listeners first!!  - so render can be captured..
11084     Roo.util.Observable.call(this);
11085     if(renderTo){
11086         this.render(renderTo);
11087     }
11088     
11089   
11090 };
11091
11092 Roo.extend(Roo.Button, Roo.util.Observable, {
11093     /**
11094      * 
11095      */
11096     
11097     /**
11098      * Read-only. True if this button is hidden
11099      * @type Boolean
11100      */
11101     hidden : false,
11102     /**
11103      * Read-only. True if this button is disabled
11104      * @type Boolean
11105      */
11106     disabled : false,
11107     /**
11108      * Read-only. True if this button is pressed (only if enableToggle = true)
11109      * @type Boolean
11110      */
11111     pressed : false,
11112
11113     /**
11114      * @cfg {Number} tabIndex 
11115      * The DOM tabIndex for this button (defaults to undefined)
11116      */
11117     tabIndex : undefined,
11118
11119     /**
11120      * @cfg {Boolean} enableToggle
11121      * True to enable pressed/not pressed toggling (defaults to false)
11122      */
11123     enableToggle: false,
11124     /**
11125      * @cfg {Mixed} menu
11126      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11127      */
11128     menu : undefined,
11129     /**
11130      * @cfg {String} menuAlign
11131      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11132      */
11133     menuAlign : "tl-bl?",
11134
11135     /**
11136      * @cfg {String} iconCls
11137      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11138      */
11139     iconCls : undefined,
11140     /**
11141      * @cfg {String} type
11142      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11143      */
11144     type : 'button',
11145
11146     // private
11147     menuClassTarget: 'tr',
11148
11149     /**
11150      * @cfg {String} clickEvent
11151      * The type of event to map to the button's event handler (defaults to 'click')
11152      */
11153     clickEvent : 'click',
11154
11155     /**
11156      * @cfg {Boolean} handleMouseEvents
11157      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11158      */
11159     handleMouseEvents : true,
11160
11161     /**
11162      * @cfg {String} tooltipType
11163      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11164      */
11165     tooltipType : 'qtip',
11166
11167     /**
11168      * @cfg {String} cls
11169      * A CSS class to apply to the button's main element.
11170      */
11171     
11172     /**
11173      * @cfg {Roo.Template} template (Optional)
11174      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11175      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11176      * require code modifications if required elements (e.g. a button) aren't present.
11177      */
11178
11179     // private
11180     render : function(renderTo){
11181         var btn;
11182         if(this.hideParent){
11183             this.parentEl = Roo.get(renderTo);
11184         }
11185         if(!this.dhconfig){
11186             if(!this.template){
11187                 if(!Roo.Button.buttonTemplate){
11188                     // hideous table template
11189                     Roo.Button.buttonTemplate = new Roo.Template(
11190                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11191                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11192                         "</tr></tbody></table>");
11193                 }
11194                 this.template = Roo.Button.buttonTemplate;
11195             }
11196             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11197             var btnEl = btn.child("button:first");
11198             btnEl.on('focus', this.onFocus, this);
11199             btnEl.on('blur', this.onBlur, this);
11200             if(this.cls){
11201                 btn.addClass(this.cls);
11202             }
11203             if(this.icon){
11204                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11205             }
11206             if(this.iconCls){
11207                 btnEl.addClass(this.iconCls);
11208                 if(!this.cls){
11209                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11210                 }
11211             }
11212             if(this.tabIndex !== undefined){
11213                 btnEl.dom.tabIndex = this.tabIndex;
11214             }
11215             if(this.tooltip){
11216                 if(typeof this.tooltip == 'object'){
11217                     Roo.QuickTips.tips(Roo.apply({
11218                           target: btnEl.id
11219                     }, this.tooltip));
11220                 } else {
11221                     btnEl.dom[this.tooltipType] = this.tooltip;
11222                 }
11223             }
11224         }else{
11225             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11226         }
11227         this.el = btn;
11228         if(this.id){
11229             this.el.dom.id = this.el.id = this.id;
11230         }
11231         if(this.menu){
11232             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11233             this.menu.on("show", this.onMenuShow, this);
11234             this.menu.on("hide", this.onMenuHide, this);
11235         }
11236         btn.addClass("x-btn");
11237         if(Roo.isIE && !Roo.isIE7){
11238             this.autoWidth.defer(1, this);
11239         }else{
11240             this.autoWidth();
11241         }
11242         if(this.handleMouseEvents){
11243             btn.on("mouseover", this.onMouseOver, this);
11244             btn.on("mouseout", this.onMouseOut, this);
11245             btn.on("mousedown", this.onMouseDown, this);
11246         }
11247         btn.on(this.clickEvent, this.onClick, this);
11248         //btn.on("mouseup", this.onMouseUp, this);
11249         if(this.hidden){
11250             this.hide();
11251         }
11252         if(this.disabled){
11253             this.disable();
11254         }
11255         Roo.ButtonToggleMgr.register(this);
11256         if(this.pressed){
11257             this.el.addClass("x-btn-pressed");
11258         }
11259         if(this.repeat){
11260             var repeater = new Roo.util.ClickRepeater(btn,
11261                 typeof this.repeat == "object" ? this.repeat : {}
11262             );
11263             repeater.on("click", this.onClick,  this);
11264         }
11265         
11266         this.fireEvent('render', this);
11267         
11268     },
11269     /**
11270      * Returns the button's underlying element
11271      * @return {Roo.Element} The element
11272      */
11273     getEl : function(){
11274         return this.el;  
11275     },
11276     
11277     /**
11278      * Destroys this Button and removes any listeners.
11279      */
11280     destroy : function(){
11281         Roo.ButtonToggleMgr.unregister(this);
11282         this.el.removeAllListeners();
11283         this.purgeListeners();
11284         this.el.remove();
11285     },
11286
11287     // private
11288     autoWidth : function(){
11289         if(this.el){
11290             this.el.setWidth("auto");
11291             if(Roo.isIE7 && Roo.isStrict){
11292                 var ib = this.el.child('button');
11293                 if(ib && ib.getWidth() > 20){
11294                     ib.clip();
11295                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11296                 }
11297             }
11298             if(this.minWidth){
11299                 if(this.hidden){
11300                     this.el.beginMeasure();
11301                 }
11302                 if(this.el.getWidth() < this.minWidth){
11303                     this.el.setWidth(this.minWidth);
11304                 }
11305                 if(this.hidden){
11306                     this.el.endMeasure();
11307                 }
11308             }
11309         }
11310     },
11311
11312     /**
11313      * Assigns this button's click handler
11314      * @param {Function} handler The function to call when the button is clicked
11315      * @param {Object} scope (optional) Scope for the function passed in
11316      */
11317     setHandler : function(handler, scope){
11318         this.handler = handler;
11319         this.scope = scope;  
11320     },
11321     
11322     /**
11323      * Sets this button's text
11324      * @param {String} text The button text
11325      */
11326     setText : function(text){
11327         this.text = text;
11328         if(this.el){
11329             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11330         }
11331         this.autoWidth();
11332     },
11333     
11334     /**
11335      * Gets the text for this button
11336      * @return {String} The button text
11337      */
11338     getText : function(){
11339         return this.text;  
11340     },
11341     
11342     /**
11343      * Show this button
11344      */
11345     show: function(){
11346         this.hidden = false;
11347         if(this.el){
11348             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11349         }
11350     },
11351     
11352     /**
11353      * Hide this button
11354      */
11355     hide: function(){
11356         this.hidden = true;
11357         if(this.el){
11358             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11359         }
11360     },
11361     
11362     /**
11363      * Convenience function for boolean show/hide
11364      * @param {Boolean} visible True to show, false to hide
11365      */
11366     setVisible: function(visible){
11367         if(visible) {
11368             this.show();
11369         }else{
11370             this.hide();
11371         }
11372     },
11373     
11374     /**
11375      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11376      * @param {Boolean} state (optional) Force a particular state
11377      */
11378     toggle : function(state){
11379         state = state === undefined ? !this.pressed : state;
11380         if(state != this.pressed){
11381             if(state){
11382                 this.el.addClass("x-btn-pressed");
11383                 this.pressed = true;
11384                 this.fireEvent("toggle", this, true);
11385             }else{
11386                 this.el.removeClass("x-btn-pressed");
11387                 this.pressed = false;
11388                 this.fireEvent("toggle", this, false);
11389             }
11390             if(this.toggleHandler){
11391                 this.toggleHandler.call(this.scope || this, this, state);
11392             }
11393         }
11394     },
11395     
11396     /**
11397      * Focus the button
11398      */
11399     focus : function(){
11400         this.el.child('button:first').focus();
11401     },
11402     
11403     /**
11404      * Disable this button
11405      */
11406     disable : function(){
11407         if(this.el){
11408             this.el.addClass("x-btn-disabled");
11409         }
11410         this.disabled = true;
11411     },
11412     
11413     /**
11414      * Enable this button
11415      */
11416     enable : function(){
11417         if(this.el){
11418             this.el.removeClass("x-btn-disabled");
11419         }
11420         this.disabled = false;
11421     },
11422
11423     /**
11424      * Convenience function for boolean enable/disable
11425      * @param {Boolean} enabled True to enable, false to disable
11426      */
11427     setDisabled : function(v){
11428         this[v !== true ? "enable" : "disable"]();
11429     },
11430
11431     // private
11432     onClick : function(e){
11433         if(e){
11434             e.preventDefault();
11435         }
11436         if(e.button != 0){
11437             return;
11438         }
11439         if(!this.disabled){
11440             if(this.enableToggle){
11441                 this.toggle();
11442             }
11443             if(this.menu && !this.menu.isVisible()){
11444                 this.menu.show(this.el, this.menuAlign);
11445             }
11446             this.fireEvent("click", this, e);
11447             if(this.handler){
11448                 this.el.removeClass("x-btn-over");
11449                 this.handler.call(this.scope || this, this, e);
11450             }
11451         }
11452     },
11453     // private
11454     onMouseOver : function(e){
11455         if(!this.disabled){
11456             this.el.addClass("x-btn-over");
11457             this.fireEvent('mouseover', this, e);
11458         }
11459     },
11460     // private
11461     onMouseOut : function(e){
11462         if(!e.within(this.el,  true)){
11463             this.el.removeClass("x-btn-over");
11464             this.fireEvent('mouseout', this, e);
11465         }
11466     },
11467     // private
11468     onFocus : function(e){
11469         if(!this.disabled){
11470             this.el.addClass("x-btn-focus");
11471         }
11472     },
11473     // private
11474     onBlur : function(e){
11475         this.el.removeClass("x-btn-focus");
11476     },
11477     // private
11478     onMouseDown : function(e){
11479         if(!this.disabled && e.button == 0){
11480             this.el.addClass("x-btn-click");
11481             Roo.get(document).on('mouseup', this.onMouseUp, this);
11482         }
11483     },
11484     // private
11485     onMouseUp : function(e){
11486         if(e.button == 0){
11487             this.el.removeClass("x-btn-click");
11488             Roo.get(document).un('mouseup', this.onMouseUp, this);
11489         }
11490     },
11491     // private
11492     onMenuShow : function(e){
11493         this.el.addClass("x-btn-menu-active");
11494     },
11495     // private
11496     onMenuHide : function(e){
11497         this.el.removeClass("x-btn-menu-active");
11498     }   
11499 });
11500
11501 // Private utility class used by Button
11502 Roo.ButtonToggleMgr = function(){
11503    var groups = {};
11504    
11505    function toggleGroup(btn, state){
11506        if(state){
11507            var g = groups[btn.toggleGroup];
11508            for(var i = 0, l = g.length; i < l; i++){
11509                if(g[i] != btn){
11510                    g[i].toggle(false);
11511                }
11512            }
11513        }
11514    }
11515    
11516    return {
11517        register : function(btn){
11518            if(!btn.toggleGroup){
11519                return;
11520            }
11521            var g = groups[btn.toggleGroup];
11522            if(!g){
11523                g = groups[btn.toggleGroup] = [];
11524            }
11525            g.push(btn);
11526            btn.on("toggle", toggleGroup);
11527        },
11528        
11529        unregister : function(btn){
11530            if(!btn.toggleGroup){
11531                return;
11532            }
11533            var g = groups[btn.toggleGroup];
11534            if(g){
11535                g.remove(btn);
11536                btn.un("toggle", toggleGroup);
11537            }
11538        }
11539    };
11540 }();/*
11541  * Based on:
11542  * Ext JS Library 1.1.1
11543  * Copyright(c) 2006-2007, Ext JS, LLC.
11544  *
11545  * Originally Released Under LGPL - original licence link has changed is not relivant.
11546  *
11547  * Fork - LGPL
11548  * <script type="text/javascript">
11549  */
11550  
11551 /**
11552  * @class Roo.SplitButton
11553  * @extends Roo.Button
11554  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11555  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11556  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11557  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11558  * @cfg {String} arrowTooltip The title attribute of the arrow
11559  * @constructor
11560  * Create a new menu button
11561  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11562  * @param {Object} config The config object
11563  */
11564 Roo.SplitButton = function(renderTo, config){
11565     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11566     /**
11567      * @event arrowclick
11568      * Fires when this button's arrow is clicked
11569      * @param {SplitButton} this
11570      * @param {EventObject} e The click event
11571      */
11572     this.addEvents({"arrowclick":true});
11573 };
11574
11575 Roo.extend(Roo.SplitButton, Roo.Button, {
11576     render : function(renderTo){
11577         // this is one sweet looking template!
11578         var tpl = new Roo.Template(
11579             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11580             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11581             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
11582             "</tbody></table></td><td>",
11583             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11584             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
11585             "</tbody></table></td></tr></table>"
11586         );
11587         var btn = tpl.append(renderTo, [this.text, this.type], true);
11588         var btnEl = btn.child("button");
11589         if(this.cls){
11590             btn.addClass(this.cls);
11591         }
11592         if(this.icon){
11593             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11594         }
11595         if(this.iconCls){
11596             btnEl.addClass(this.iconCls);
11597             if(!this.cls){
11598                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11599             }
11600         }
11601         this.el = btn;
11602         if(this.handleMouseEvents){
11603             btn.on("mouseover", this.onMouseOver, this);
11604             btn.on("mouseout", this.onMouseOut, this);
11605             btn.on("mousedown", this.onMouseDown, this);
11606             btn.on("mouseup", this.onMouseUp, this);
11607         }
11608         btn.on(this.clickEvent, this.onClick, this);
11609         if(this.tooltip){
11610             if(typeof this.tooltip == 'object'){
11611                 Roo.QuickTips.tips(Roo.apply({
11612                       target: btnEl.id
11613                 }, this.tooltip));
11614             } else {
11615                 btnEl.dom[this.tooltipType] = this.tooltip;
11616             }
11617         }
11618         if(this.arrowTooltip){
11619             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11620         }
11621         if(this.hidden){
11622             this.hide();
11623         }
11624         if(this.disabled){
11625             this.disable();
11626         }
11627         if(this.pressed){
11628             this.el.addClass("x-btn-pressed");
11629         }
11630         if(Roo.isIE && !Roo.isIE7){
11631             this.autoWidth.defer(1, this);
11632         }else{
11633             this.autoWidth();
11634         }
11635         if(this.menu){
11636             this.menu.on("show", this.onMenuShow, this);
11637             this.menu.on("hide", this.onMenuHide, this);
11638         }
11639         this.fireEvent('render', this);
11640     },
11641
11642     // private
11643     autoWidth : function(){
11644         if(this.el){
11645             var tbl = this.el.child("table:first");
11646             var tbl2 = this.el.child("table:last");
11647             this.el.setWidth("auto");
11648             tbl.setWidth("auto");
11649             if(Roo.isIE7 && Roo.isStrict){
11650                 var ib = this.el.child('button:first');
11651                 if(ib && ib.getWidth() > 20){
11652                     ib.clip();
11653                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11654                 }
11655             }
11656             if(this.minWidth){
11657                 if(this.hidden){
11658                     this.el.beginMeasure();
11659                 }
11660                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11661                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11662                 }
11663                 if(this.hidden){
11664                     this.el.endMeasure();
11665                 }
11666             }
11667             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11668         } 
11669     },
11670     /**
11671      * Sets this button's click handler
11672      * @param {Function} handler The function to call when the button is clicked
11673      * @param {Object} scope (optional) Scope for the function passed above
11674      */
11675     setHandler : function(handler, scope){
11676         this.handler = handler;
11677         this.scope = scope;  
11678     },
11679     
11680     /**
11681      * Sets this button's arrow click handler
11682      * @param {Function} handler The function to call when the arrow is clicked
11683      * @param {Object} scope (optional) Scope for the function passed above
11684      */
11685     setArrowHandler : function(handler, scope){
11686         this.arrowHandler = handler;
11687         this.scope = scope;  
11688     },
11689     
11690     /**
11691      * Focus the button
11692      */
11693     focus : function(){
11694         if(this.el){
11695             this.el.child("button:first").focus();
11696         }
11697     },
11698
11699     // private
11700     onClick : function(e){
11701         e.preventDefault();
11702         if(!this.disabled){
11703             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11704                 if(this.menu && !this.menu.isVisible()){
11705                     this.menu.show(this.el, this.menuAlign);
11706                 }
11707                 this.fireEvent("arrowclick", this, e);
11708                 if(this.arrowHandler){
11709                     this.arrowHandler.call(this.scope || this, this, e);
11710                 }
11711             }else{
11712                 this.fireEvent("click", this, e);
11713                 if(this.handler){
11714                     this.handler.call(this.scope || this, this, e);
11715                 }
11716             }
11717         }
11718     },
11719     // private
11720     onMouseDown : function(e){
11721         if(!this.disabled){
11722             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11723         }
11724     },
11725     // private
11726     onMouseUp : function(e){
11727         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11728     }   
11729 });
11730
11731
11732 // backwards compat
11733 Roo.MenuButton = Roo.SplitButton;/*
11734  * Based on:
11735  * Ext JS Library 1.1.1
11736  * Copyright(c) 2006-2007, Ext JS, LLC.
11737  *
11738  * Originally Released Under LGPL - original licence link has changed is not relivant.
11739  *
11740  * Fork - LGPL
11741  * <script type="text/javascript">
11742  */
11743
11744 /**
11745  * @class Roo.Toolbar
11746  * Basic Toolbar class.
11747  * @constructor
11748  * Creates a new Toolbar
11749  * @param {Object} container The config object
11750  */ 
11751 Roo.Toolbar = function(container, buttons, config)
11752 {
11753     /// old consturctor format still supported..
11754     if(container instanceof Array){ // omit the container for later rendering
11755         buttons = container;
11756         config = buttons;
11757         container = null;
11758     }
11759     if (typeof(container) == 'object' && container.xtype) {
11760         config = container;
11761         container = config.container;
11762         buttons = config.buttons || []; // not really - use items!!
11763     }
11764     var xitems = [];
11765     if (config && config.items) {
11766         xitems = config.items;
11767         delete config.items;
11768     }
11769     Roo.apply(this, config);
11770     this.buttons = buttons;
11771     
11772     if(container){
11773         this.render(container);
11774     }
11775     this.xitems = xitems;
11776     Roo.each(xitems, function(b) {
11777         this.add(b);
11778     }, this);
11779     
11780 };
11781
11782 Roo.Toolbar.prototype = {
11783     /**
11784      * @cfg {Array} items
11785      * array of button configs or elements to add (will be converted to a MixedCollection)
11786      */
11787     
11788     /**
11789      * @cfg {String/HTMLElement/Element} container
11790      * The id or element that will contain the toolbar
11791      */
11792     // private
11793     render : function(ct){
11794         this.el = Roo.get(ct);
11795         if(this.cls){
11796             this.el.addClass(this.cls);
11797         }
11798         // using a table allows for vertical alignment
11799         // 100% width is needed by Safari...
11800         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11801         this.tr = this.el.child("tr", true);
11802         var autoId = 0;
11803         this.items = new Roo.util.MixedCollection(false, function(o){
11804             return o.id || ("item" + (++autoId));
11805         });
11806         if(this.buttons){
11807             this.add.apply(this, this.buttons);
11808             delete this.buttons;
11809         }
11810     },
11811
11812     /**
11813      * Adds element(s) to the toolbar -- this function takes a variable number of 
11814      * arguments of mixed type and adds them to the toolbar.
11815      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11816      * <ul>
11817      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11818      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11819      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11820      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11821      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11822      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11823      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11824      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11825      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11826      * </ul>
11827      * @param {Mixed} arg2
11828      * @param {Mixed} etc.
11829      */
11830     add : function(){
11831         var a = arguments, l = a.length;
11832         for(var i = 0; i < l; i++){
11833             this._add(a[i]);
11834         }
11835     },
11836     // private..
11837     _add : function(el) {
11838         
11839         if (el.xtype) {
11840             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11841         }
11842         
11843         if (el.applyTo){ // some kind of form field
11844             return this.addField(el);
11845         } 
11846         if (el.render){ // some kind of Toolbar.Item
11847             return this.addItem(el);
11848         }
11849         if (typeof el == "string"){ // string
11850             if(el == "separator" || el == "-"){
11851                 return this.addSeparator();
11852             }
11853             if (el == " "){
11854                 return this.addSpacer();
11855             }
11856             if(el == "->"){
11857                 return this.addFill();
11858             }
11859             return this.addText(el);
11860             
11861         }
11862         if(el.tagName){ // element
11863             return this.addElement(el);
11864         }
11865         if(typeof el == "object"){ // must be button config?
11866             return this.addButton(el);
11867         }
11868         // and now what?!?!
11869         return false;
11870         
11871     },
11872     
11873     /**
11874      * Add an Xtype element
11875      * @param {Object} xtype Xtype Object
11876      * @return {Object} created Object
11877      */
11878     addxtype : function(e){
11879         return this.add(e);  
11880     },
11881     
11882     /**
11883      * Returns the Element for this toolbar.
11884      * @return {Roo.Element}
11885      */
11886     getEl : function(){
11887         return this.el;  
11888     },
11889     
11890     /**
11891      * Adds a separator
11892      * @return {Roo.Toolbar.Item} The separator item
11893      */
11894     addSeparator : function(){
11895         return this.addItem(new Roo.Toolbar.Separator());
11896     },
11897
11898     /**
11899      * Adds a spacer element
11900      * @return {Roo.Toolbar.Spacer} The spacer item
11901      */
11902     addSpacer : function(){
11903         return this.addItem(new Roo.Toolbar.Spacer());
11904     },
11905
11906     /**
11907      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11908      * @return {Roo.Toolbar.Fill} The fill item
11909      */
11910     addFill : function(){
11911         return this.addItem(new Roo.Toolbar.Fill());
11912     },
11913
11914     /**
11915      * Adds any standard HTML element to the toolbar
11916      * @param {String/HTMLElement/Element} el The element or id of the element to add
11917      * @return {Roo.Toolbar.Item} The element's item
11918      */
11919     addElement : function(el){
11920         return this.addItem(new Roo.Toolbar.Item(el));
11921     },
11922     /**
11923      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11924      * @type Roo.util.MixedCollection  
11925      */
11926     items : false,
11927      
11928     /**
11929      * Adds any Toolbar.Item or subclass
11930      * @param {Roo.Toolbar.Item} item
11931      * @return {Roo.Toolbar.Item} The item
11932      */
11933     addItem : function(item){
11934         var td = this.nextBlock();
11935         item.render(td);
11936         this.items.add(item);
11937         return item;
11938     },
11939     
11940     /**
11941      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11942      * @param {Object/Array} config A button config or array of configs
11943      * @return {Roo.Toolbar.Button/Array}
11944      */
11945     addButton : function(config){
11946         if(config instanceof Array){
11947             var buttons = [];
11948             for(var i = 0, len = config.length; i < len; i++) {
11949                 buttons.push(this.addButton(config[i]));
11950             }
11951             return buttons;
11952         }
11953         var b = config;
11954         if(!(config instanceof Roo.Toolbar.Button)){
11955             b = config.split ?
11956                 new Roo.Toolbar.SplitButton(config) :
11957                 new Roo.Toolbar.Button(config);
11958         }
11959         var td = this.nextBlock();
11960         b.render(td);
11961         this.items.add(b);
11962         return b;
11963     },
11964     
11965     /**
11966      * Adds text to the toolbar
11967      * @param {String} text The text to add
11968      * @return {Roo.Toolbar.Item} The element's item
11969      */
11970     addText : function(text){
11971         return this.addItem(new Roo.Toolbar.TextItem(text));
11972     },
11973     
11974     /**
11975      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11976      * @param {Number} index The index where the item is to be inserted
11977      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11978      * @return {Roo.Toolbar.Button/Item}
11979      */
11980     insertButton : function(index, item){
11981         if(item instanceof Array){
11982             var buttons = [];
11983             for(var i = 0, len = item.length; i < len; i++) {
11984                buttons.push(this.insertButton(index + i, item[i]));
11985             }
11986             return buttons;
11987         }
11988         if (!(item instanceof Roo.Toolbar.Button)){
11989            item = new Roo.Toolbar.Button(item);
11990         }
11991         var td = document.createElement("td");
11992         this.tr.insertBefore(td, this.tr.childNodes[index]);
11993         item.render(td);
11994         this.items.insert(index, item);
11995         return item;
11996     },
11997     
11998     /**
11999      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12000      * @param {Object} config
12001      * @return {Roo.Toolbar.Item} The element's item
12002      */
12003     addDom : function(config, returnEl){
12004         var td = this.nextBlock();
12005         Roo.DomHelper.overwrite(td, config);
12006         var ti = new Roo.Toolbar.Item(td.firstChild);
12007         ti.render(td);
12008         this.items.add(ti);
12009         return ti;
12010     },
12011
12012     /**
12013      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12014      * @type Roo.util.MixedCollection  
12015      */
12016     fields : false,
12017     
12018     /**
12019      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12020      * Note: the field should not have been rendered yet. For a field that has already been
12021      * rendered, use {@link #addElement}.
12022      * @param {Roo.form.Field} field
12023      * @return {Roo.ToolbarItem}
12024      */
12025      
12026       
12027     addField : function(field) {
12028         if (!this.fields) {
12029             var autoId = 0;
12030             this.fields = new Roo.util.MixedCollection(false, function(o){
12031                 return o.id || ("item" + (++autoId));
12032             });
12033
12034         }
12035         
12036         var td = this.nextBlock();
12037         field.render(td);
12038         var ti = new Roo.Toolbar.Item(td.firstChild);
12039         ti.render(td);
12040         this.items.add(ti);
12041         this.fields.add(field);
12042         return ti;
12043     },
12044     /**
12045      * Hide the toolbar
12046      * @method hide
12047      */
12048      
12049       
12050     hide : function()
12051     {
12052         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12053         this.el.child('div').hide();
12054     },
12055     /**
12056      * Show the toolbar
12057      * @method show
12058      */
12059     show : function()
12060     {
12061         this.el.child('div').show();
12062     },
12063       
12064     // private
12065     nextBlock : function(){
12066         var td = document.createElement("td");
12067         this.tr.appendChild(td);
12068         return td;
12069     },
12070
12071     // private
12072     destroy : function(){
12073         if(this.items){ // rendered?
12074             Roo.destroy.apply(Roo, this.items.items);
12075         }
12076         if(this.fields){ // rendered?
12077             Roo.destroy.apply(Roo, this.fields.items);
12078         }
12079         Roo.Element.uncache(this.el, this.tr);
12080     }
12081 };
12082
12083 /**
12084  * @class Roo.Toolbar.Item
12085  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12086  * @constructor
12087  * Creates a new Item
12088  * @param {HTMLElement} el 
12089  */
12090 Roo.Toolbar.Item = function(el){
12091     this.el = Roo.getDom(el);
12092     this.id = Roo.id(this.el);
12093     this.hidden = false;
12094 };
12095
12096 Roo.Toolbar.Item.prototype = {
12097     
12098     /**
12099      * Get this item's HTML Element
12100      * @return {HTMLElement}
12101      */
12102     getEl : function(){
12103        return this.el;  
12104     },
12105
12106     // private
12107     render : function(td){
12108         this.td = td;
12109         td.appendChild(this.el);
12110     },
12111     
12112     /**
12113      * Removes and destroys this item.
12114      */
12115     destroy : function(){
12116         this.td.parentNode.removeChild(this.td);
12117     },
12118     
12119     /**
12120      * Shows this item.
12121      */
12122     show: function(){
12123         this.hidden = false;
12124         this.td.style.display = "";
12125     },
12126     
12127     /**
12128      * Hides this item.
12129      */
12130     hide: function(){
12131         this.hidden = true;
12132         this.td.style.display = "none";
12133     },
12134     
12135     /**
12136      * Convenience function for boolean show/hide.
12137      * @param {Boolean} visible true to show/false to hide
12138      */
12139     setVisible: function(visible){
12140         if(visible) {
12141             this.show();
12142         }else{
12143             this.hide();
12144         }
12145     },
12146     
12147     /**
12148      * Try to focus this item.
12149      */
12150     focus : function(){
12151         Roo.fly(this.el).focus();
12152     },
12153     
12154     /**
12155      * Disables this item.
12156      */
12157     disable : function(){
12158         Roo.fly(this.td).addClass("x-item-disabled");
12159         this.disabled = true;
12160         this.el.disabled = true;
12161     },
12162     
12163     /**
12164      * Enables this item.
12165      */
12166     enable : function(){
12167         Roo.fly(this.td).removeClass("x-item-disabled");
12168         this.disabled = false;
12169         this.el.disabled = false;
12170     }
12171 };
12172
12173
12174 /**
12175  * @class Roo.Toolbar.Separator
12176  * @extends Roo.Toolbar.Item
12177  * A simple toolbar separator class
12178  * @constructor
12179  * Creates a new Separator
12180  */
12181 Roo.Toolbar.Separator = function(){
12182     var s = document.createElement("span");
12183     s.className = "ytb-sep";
12184     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12185 };
12186 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12187     enable:Roo.emptyFn,
12188     disable:Roo.emptyFn,
12189     focus:Roo.emptyFn
12190 });
12191
12192 /**
12193  * @class Roo.Toolbar.Spacer
12194  * @extends Roo.Toolbar.Item
12195  * A simple element that adds extra horizontal space to a toolbar.
12196  * @constructor
12197  * Creates a new Spacer
12198  */
12199 Roo.Toolbar.Spacer = function(){
12200     var s = document.createElement("div");
12201     s.className = "ytb-spacer";
12202     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12203 };
12204 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12205     enable:Roo.emptyFn,
12206     disable:Roo.emptyFn,
12207     focus:Roo.emptyFn
12208 });
12209
12210 /**
12211  * @class Roo.Toolbar.Fill
12212  * @extends Roo.Toolbar.Spacer
12213  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12214  * @constructor
12215  * Creates a new Spacer
12216  */
12217 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12218     // private
12219     render : function(td){
12220         td.style.width = '100%';
12221         Roo.Toolbar.Fill.superclass.render.call(this, td);
12222     }
12223 });
12224
12225 /**
12226  * @class Roo.Toolbar.TextItem
12227  * @extends Roo.Toolbar.Item
12228  * A simple class that renders text directly into a toolbar.
12229  * @constructor
12230  * Creates a new TextItem
12231  * @param {String} text
12232  */
12233 Roo.Toolbar.TextItem = function(text){
12234     if (typeof(text) == 'object') {
12235         text = text.text;
12236     }
12237     var s = document.createElement("span");
12238     s.className = "ytb-text";
12239     s.innerHTML = text;
12240     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12241 };
12242 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12243     enable:Roo.emptyFn,
12244     disable:Roo.emptyFn,
12245     focus:Roo.emptyFn
12246 });
12247
12248 /**
12249  * @class Roo.Toolbar.Button
12250  * @extends Roo.Button
12251  * A button that renders into a toolbar.
12252  * @constructor
12253  * Creates a new Button
12254  * @param {Object} config A standard {@link Roo.Button} config object
12255  */
12256 Roo.Toolbar.Button = function(config){
12257     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12258 };
12259 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12260     render : function(td){
12261         this.td = td;
12262         Roo.Toolbar.Button.superclass.render.call(this, td);
12263     },
12264     
12265     /**
12266      * Removes and destroys this button
12267      */
12268     destroy : function(){
12269         Roo.Toolbar.Button.superclass.destroy.call(this);
12270         this.td.parentNode.removeChild(this.td);
12271     },
12272     
12273     /**
12274      * Shows this button
12275      */
12276     show: function(){
12277         this.hidden = false;
12278         this.td.style.display = "";
12279     },
12280     
12281     /**
12282      * Hides this button
12283      */
12284     hide: function(){
12285         this.hidden = true;
12286         this.td.style.display = "none";
12287     },
12288
12289     /**
12290      * Disables this item
12291      */
12292     disable : function(){
12293         Roo.fly(this.td).addClass("x-item-disabled");
12294         this.disabled = true;
12295     },
12296
12297     /**
12298      * Enables this item
12299      */
12300     enable : function(){
12301         Roo.fly(this.td).removeClass("x-item-disabled");
12302         this.disabled = false;
12303     }
12304 });
12305 // backwards compat
12306 Roo.ToolbarButton = Roo.Toolbar.Button;
12307
12308 /**
12309  * @class Roo.Toolbar.SplitButton
12310  * @extends Roo.SplitButton
12311  * A menu button that renders into a toolbar.
12312  * @constructor
12313  * Creates a new SplitButton
12314  * @param {Object} config A standard {@link Roo.SplitButton} config object
12315  */
12316 Roo.Toolbar.SplitButton = function(config){
12317     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12318 };
12319 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12320     render : function(td){
12321         this.td = td;
12322         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12323     },
12324     
12325     /**
12326      * Removes and destroys this button
12327      */
12328     destroy : function(){
12329         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12330         this.td.parentNode.removeChild(this.td);
12331     },
12332     
12333     /**
12334      * Shows this button
12335      */
12336     show: function(){
12337         this.hidden = false;
12338         this.td.style.display = "";
12339     },
12340     
12341     /**
12342      * Hides this button
12343      */
12344     hide: function(){
12345         this.hidden = true;
12346         this.td.style.display = "none";
12347     }
12348 });
12349
12350 // backwards compat
12351 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12352  * Based on:
12353  * Ext JS Library 1.1.1
12354  * Copyright(c) 2006-2007, Ext JS, LLC.
12355  *
12356  * Originally Released Under LGPL - original licence link has changed is not relivant.
12357  *
12358  * Fork - LGPL
12359  * <script type="text/javascript">
12360  */
12361  
12362 /**
12363  * @class Roo.PagingToolbar
12364  * @extends Roo.Toolbar
12365  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12366  * @constructor
12367  * Create a new PagingToolbar
12368  * @param {Object} config The config object
12369  */
12370 Roo.PagingToolbar = function(el, ds, config)
12371 {
12372     // old args format still supported... - xtype is prefered..
12373     if (typeof(el) == 'object' && el.xtype) {
12374         // created from xtype...
12375         config = el;
12376         ds = el.dataSource;
12377         el = config.container;
12378     }
12379     var items = [];
12380     if (config.items) {
12381         items = config.items;
12382         config.items = [];
12383     }
12384     
12385     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12386     this.ds = ds;
12387     this.cursor = 0;
12388     this.renderButtons(this.el);
12389     this.bind(ds);
12390     
12391     // supprot items array.
12392    
12393     Roo.each(items, function(e) {
12394         this.add(Roo.factory(e));
12395     },this);
12396     
12397 };
12398
12399 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12400     /**
12401      * @cfg {Roo.data.Store} dataSource
12402      * The underlying data store providing the paged data
12403      */
12404     /**
12405      * @cfg {String/HTMLElement/Element} container
12406      * container The id or element that will contain the toolbar
12407      */
12408     /**
12409      * @cfg {Boolean} displayInfo
12410      * True to display the displayMsg (defaults to false)
12411      */
12412     /**
12413      * @cfg {Number} pageSize
12414      * The number of records to display per page (defaults to 20)
12415      */
12416     pageSize: 20,
12417     /**
12418      * @cfg {String} displayMsg
12419      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12420      */
12421     displayMsg : 'Displaying {0} - {1} of {2}',
12422     /**
12423      * @cfg {String} emptyMsg
12424      * The message to display when no records are found (defaults to "No data to display")
12425      */
12426     emptyMsg : 'No data to display',
12427     /**
12428      * Customizable piece of the default paging text (defaults to "Page")
12429      * @type String
12430      */
12431     beforePageText : "Page",
12432     /**
12433      * Customizable piece of the default paging text (defaults to "of %0")
12434      * @type String
12435      */
12436     afterPageText : "of {0}",
12437     /**
12438      * Customizable piece of the default paging text (defaults to "First Page")
12439      * @type String
12440      */
12441     firstText : "First Page",
12442     /**
12443      * Customizable piece of the default paging text (defaults to "Previous Page")
12444      * @type String
12445      */
12446     prevText : "Previous Page",
12447     /**
12448      * Customizable piece of the default paging text (defaults to "Next Page")
12449      * @type String
12450      */
12451     nextText : "Next Page",
12452     /**
12453      * Customizable piece of the default paging text (defaults to "Last Page")
12454      * @type String
12455      */
12456     lastText : "Last Page",
12457     /**
12458      * Customizable piece of the default paging text (defaults to "Refresh")
12459      * @type String
12460      */
12461     refreshText : "Refresh",
12462
12463     // private
12464     renderButtons : function(el){
12465         Roo.PagingToolbar.superclass.render.call(this, el);
12466         this.first = this.addButton({
12467             tooltip: this.firstText,
12468             cls: "x-btn-icon x-grid-page-first",
12469             disabled: true,
12470             handler: this.onClick.createDelegate(this, ["first"])
12471         });
12472         this.prev = this.addButton({
12473             tooltip: this.prevText,
12474             cls: "x-btn-icon x-grid-page-prev",
12475             disabled: true,
12476             handler: this.onClick.createDelegate(this, ["prev"])
12477         });
12478         //this.addSeparator();
12479         this.add(this.beforePageText);
12480         this.field = Roo.get(this.addDom({
12481            tag: "input",
12482            type: "text",
12483            size: "3",
12484            value: "1",
12485            cls: "x-grid-page-number"
12486         }).el);
12487         this.field.on("keydown", this.onPagingKeydown, this);
12488         this.field.on("focus", function(){this.dom.select();});
12489         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12490         this.field.setHeight(18);
12491         //this.addSeparator();
12492         this.next = this.addButton({
12493             tooltip: this.nextText,
12494             cls: "x-btn-icon x-grid-page-next",
12495             disabled: true,
12496             handler: this.onClick.createDelegate(this, ["next"])
12497         });
12498         this.last = this.addButton({
12499             tooltip: this.lastText,
12500             cls: "x-btn-icon x-grid-page-last",
12501             disabled: true,
12502             handler: this.onClick.createDelegate(this, ["last"])
12503         });
12504         //this.addSeparator();
12505         this.loading = this.addButton({
12506             tooltip: this.refreshText,
12507             cls: "x-btn-icon x-grid-loading",
12508             handler: this.onClick.createDelegate(this, ["refresh"])
12509         });
12510
12511         if(this.displayInfo){
12512             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12513         }
12514     },
12515
12516     // private
12517     updateInfo : function(){
12518         if(this.displayEl){
12519             var count = this.ds.getCount();
12520             var msg = count == 0 ?
12521                 this.emptyMsg :
12522                 String.format(
12523                     this.displayMsg,
12524                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12525                 );
12526             this.displayEl.update(msg);
12527         }
12528     },
12529
12530     // private
12531     onLoad : function(ds, r, o){
12532        this.cursor = o.params ? o.params.start : 0;
12533        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12534
12535        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12536        this.field.dom.value = ap;
12537        this.first.setDisabled(ap == 1);
12538        this.prev.setDisabled(ap == 1);
12539        this.next.setDisabled(ap == ps);
12540        this.last.setDisabled(ap == ps);
12541        this.loading.enable();
12542        this.updateInfo();
12543     },
12544
12545     // private
12546     getPageData : function(){
12547         var total = this.ds.getTotalCount();
12548         return {
12549             total : total,
12550             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12551             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12552         };
12553     },
12554
12555     // private
12556     onLoadError : function(){
12557         this.loading.enable();
12558     },
12559
12560     // private
12561     onPagingKeydown : function(e){
12562         var k = e.getKey();
12563         var d = this.getPageData();
12564         if(k == e.RETURN){
12565             var v = this.field.dom.value, pageNum;
12566             if(!v || isNaN(pageNum = parseInt(v, 10))){
12567                 this.field.dom.value = d.activePage;
12568                 return;
12569             }
12570             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12571             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12572             e.stopEvent();
12573         }
12574         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))
12575         {
12576           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12577           this.field.dom.value = pageNum;
12578           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12579           e.stopEvent();
12580         }
12581         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12582         {
12583           var v = this.field.dom.value, pageNum; 
12584           var increment = (e.shiftKey) ? 10 : 1;
12585           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12586             increment *= -1;
12587           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12588             this.field.dom.value = d.activePage;
12589             return;
12590           }
12591           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12592           {
12593             this.field.dom.value = parseInt(v, 10) + increment;
12594             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12595             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12596           }
12597           e.stopEvent();
12598         }
12599     },
12600
12601     // private
12602     beforeLoad : function(){
12603         if(this.loading){
12604             this.loading.disable();
12605         }
12606     },
12607
12608     // private
12609     onClick : function(which){
12610         var ds = this.ds;
12611         switch(which){
12612             case "first":
12613                 ds.load({params:{start: 0, limit: this.pageSize}});
12614             break;
12615             case "prev":
12616                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12617             break;
12618             case "next":
12619                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12620             break;
12621             case "last":
12622                 var total = ds.getTotalCount();
12623                 var extra = total % this.pageSize;
12624                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12625                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12626             break;
12627             case "refresh":
12628                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12629             break;
12630         }
12631     },
12632
12633     /**
12634      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12635      * @param {Roo.data.Store} store The data store to unbind
12636      */
12637     unbind : function(ds){
12638         ds.un("beforeload", this.beforeLoad, this);
12639         ds.un("load", this.onLoad, this);
12640         ds.un("loadexception", this.onLoadError, this);
12641         ds.un("remove", this.updateInfo, this);
12642         ds.un("add", this.updateInfo, this);
12643         this.ds = undefined;
12644     },
12645
12646     /**
12647      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12648      * @param {Roo.data.Store} store The data store to bind
12649      */
12650     bind : function(ds){
12651         ds.on("beforeload", this.beforeLoad, this);
12652         ds.on("load", this.onLoad, this);
12653         ds.on("loadexception", this.onLoadError, this);
12654         ds.on("remove", this.updateInfo, this);
12655         ds.on("add", this.updateInfo, this);
12656         this.ds = ds;
12657     }
12658 });/*
12659  * Based on:
12660  * Ext JS Library 1.1.1
12661  * Copyright(c) 2006-2007, Ext JS, LLC.
12662  *
12663  * Originally Released Under LGPL - original licence link has changed is not relivant.
12664  *
12665  * Fork - LGPL
12666  * <script type="text/javascript">
12667  */
12668
12669 /**
12670  * @class Roo.Resizable
12671  * @extends Roo.util.Observable
12672  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12673  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12674  * 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
12675  * the element will be wrapped for you automatically.</p>
12676  * <p>Here is the list of valid resize handles:</p>
12677  * <pre>
12678 Value   Description
12679 ------  -------------------
12680  'n'     north
12681  's'     south
12682  'e'     east
12683  'w'     west
12684  'nw'    northwest
12685  'sw'    southwest
12686  'se'    southeast
12687  'ne'    northeast
12688  'hd'    horizontal drag
12689  'all'   all
12690 </pre>
12691  * <p>Here's an example showing the creation of a typical Resizable:</p>
12692  * <pre><code>
12693 var resizer = new Roo.Resizable("element-id", {
12694     handles: 'all',
12695     minWidth: 200,
12696     minHeight: 100,
12697     maxWidth: 500,
12698     maxHeight: 400,
12699     pinned: true
12700 });
12701 resizer.on("resize", myHandler);
12702 </code></pre>
12703  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12704  * resizer.east.setDisplayed(false);</p>
12705  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12706  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12707  * resize operation's new size (defaults to [0, 0])
12708  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12709  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12710  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12711  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12712  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12713  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12714  * @cfg {Number} width The width of the element in pixels (defaults to null)
12715  * @cfg {Number} height The height of the element in pixels (defaults to null)
12716  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12717  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12718  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12719  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12720  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12721  * in favor of the handles config option (defaults to false)
12722  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12723  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12724  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12725  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12726  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12727  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12728  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12729  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12730  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12731  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12732  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12733  * @constructor
12734  * Create a new resizable component
12735  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12736  * @param {Object} config configuration options
12737   */
12738 Roo.Resizable = function(el, config)
12739 {
12740     this.el = Roo.get(el);
12741
12742     if(config && config.wrap){
12743         config.resizeChild = this.el;
12744         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12745         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12746         this.el.setStyle("overflow", "hidden");
12747         this.el.setPositioning(config.resizeChild.getPositioning());
12748         config.resizeChild.clearPositioning();
12749         if(!config.width || !config.height){
12750             var csize = config.resizeChild.getSize();
12751             this.el.setSize(csize.width, csize.height);
12752         }
12753         if(config.pinned && !config.adjustments){
12754             config.adjustments = "auto";
12755         }
12756     }
12757
12758     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12759     this.proxy.unselectable();
12760     this.proxy.enableDisplayMode('block');
12761
12762     Roo.apply(this, config);
12763
12764     if(this.pinned){
12765         this.disableTrackOver = true;
12766         this.el.addClass("x-resizable-pinned");
12767     }
12768     // if the element isn't positioned, make it relative
12769     var position = this.el.getStyle("position");
12770     if(position != "absolute" && position != "fixed"){
12771         this.el.setStyle("position", "relative");
12772     }
12773     if(!this.handles){ // no handles passed, must be legacy style
12774         this.handles = 's,e,se';
12775         if(this.multiDirectional){
12776             this.handles += ',n,w';
12777         }
12778     }
12779     if(this.handles == "all"){
12780         this.handles = "n s e w ne nw se sw";
12781     }
12782     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12783     var ps = Roo.Resizable.positions;
12784     for(var i = 0, len = hs.length; i < len; i++){
12785         if(hs[i] && ps[hs[i]]){
12786             var pos = ps[hs[i]];
12787             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12788         }
12789     }
12790     // legacy
12791     this.corner = this.southeast;
12792     
12793     // updateBox = the box can move..
12794     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12795         this.updateBox = true;
12796     }
12797
12798     this.activeHandle = null;
12799
12800     if(this.resizeChild){
12801         if(typeof this.resizeChild == "boolean"){
12802             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12803         }else{
12804             this.resizeChild = Roo.get(this.resizeChild, true);
12805         }
12806     }
12807     
12808     if(this.adjustments == "auto"){
12809         var rc = this.resizeChild;
12810         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12811         if(rc && (hw || hn)){
12812             rc.position("relative");
12813             rc.setLeft(hw ? hw.el.getWidth() : 0);
12814             rc.setTop(hn ? hn.el.getHeight() : 0);
12815         }
12816         this.adjustments = [
12817             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12818             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12819         ];
12820     }
12821
12822     if(this.draggable){
12823         this.dd = this.dynamic ?
12824             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12825         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12826     }
12827
12828     // public events
12829     this.addEvents({
12830         /**
12831          * @event beforeresize
12832          * Fired before resize is allowed. Set enabled to false to cancel resize.
12833          * @param {Roo.Resizable} this
12834          * @param {Roo.EventObject} e The mousedown event
12835          */
12836         "beforeresize" : true,
12837         /**
12838          * @event resizing
12839          * Fired a resizing.
12840          * @param {Roo.Resizable} this
12841          * @param {Number} x The new x position
12842          * @param {Number} y The new y position
12843          * @param {Number} w The new w width
12844          * @param {Number} h The new h hight
12845          * @param {Roo.EventObject} e The mouseup event
12846          */
12847         "resizing" : true,
12848         /**
12849          * @event resize
12850          * Fired after a resize.
12851          * @param {Roo.Resizable} this
12852          * @param {Number} width The new width
12853          * @param {Number} height The new height
12854          * @param {Roo.EventObject} e The mouseup event
12855          */
12856         "resize" : true
12857     });
12858
12859     if(this.width !== null && this.height !== null){
12860         this.resizeTo(this.width, this.height);
12861     }else{
12862         this.updateChildSize();
12863     }
12864     if(Roo.isIE){
12865         this.el.dom.style.zoom = 1;
12866     }
12867     Roo.Resizable.superclass.constructor.call(this);
12868 };
12869
12870 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12871         resizeChild : false,
12872         adjustments : [0, 0],
12873         minWidth : 5,
12874         minHeight : 5,
12875         maxWidth : 10000,
12876         maxHeight : 10000,
12877         enabled : true,
12878         animate : false,
12879         duration : .35,
12880         dynamic : false,
12881         handles : false,
12882         multiDirectional : false,
12883         disableTrackOver : false,
12884         easing : 'easeOutStrong',
12885         widthIncrement : 0,
12886         heightIncrement : 0,
12887         pinned : false,
12888         width : null,
12889         height : null,
12890         preserveRatio : false,
12891         transparent: false,
12892         minX: 0,
12893         minY: 0,
12894         draggable: false,
12895
12896         /**
12897          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12898          */
12899         constrainTo: undefined,
12900         /**
12901          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12902          */
12903         resizeRegion: undefined,
12904
12905
12906     /**
12907      * Perform a manual resize
12908      * @param {Number} width
12909      * @param {Number} height
12910      */
12911     resizeTo : function(width, height){
12912         this.el.setSize(width, height);
12913         this.updateChildSize();
12914         this.fireEvent("resize", this, width, height, null);
12915     },
12916
12917     // private
12918     startSizing : function(e, handle){
12919         this.fireEvent("beforeresize", this, e);
12920         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12921
12922             if(!this.overlay){
12923                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12924                 this.overlay.unselectable();
12925                 this.overlay.enableDisplayMode("block");
12926                 this.overlay.on("mousemove", this.onMouseMove, this);
12927                 this.overlay.on("mouseup", this.onMouseUp, this);
12928             }
12929             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12930
12931             this.resizing = true;
12932             this.startBox = this.el.getBox();
12933             this.startPoint = e.getXY();
12934             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12935                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12936
12937             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12938             this.overlay.show();
12939
12940             if(this.constrainTo) {
12941                 var ct = Roo.get(this.constrainTo);
12942                 this.resizeRegion = ct.getRegion().adjust(
12943                     ct.getFrameWidth('t'),
12944                     ct.getFrameWidth('l'),
12945                     -ct.getFrameWidth('b'),
12946                     -ct.getFrameWidth('r')
12947                 );
12948             }
12949
12950             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12951             this.proxy.show();
12952             this.proxy.setBox(this.startBox);
12953             if(!this.dynamic){
12954                 this.proxy.setStyle('visibility', 'visible');
12955             }
12956         }
12957     },
12958
12959     // private
12960     onMouseDown : function(handle, e){
12961         if(this.enabled){
12962             e.stopEvent();
12963             this.activeHandle = handle;
12964             this.startSizing(e, handle);
12965         }
12966     },
12967
12968     // private
12969     onMouseUp : function(e){
12970         var size = this.resizeElement();
12971         this.resizing = false;
12972         this.handleOut();
12973         this.overlay.hide();
12974         this.proxy.hide();
12975         this.fireEvent("resize", this, size.width, size.height, e);
12976     },
12977
12978     // private
12979     updateChildSize : function(){
12980         
12981         if(this.resizeChild){
12982             var el = this.el;
12983             var child = this.resizeChild;
12984             var adj = this.adjustments;
12985             if(el.dom.offsetWidth){
12986                 var b = el.getSize(true);
12987                 child.setSize(b.width+adj[0], b.height+adj[1]);
12988             }
12989             // Second call here for IE
12990             // The first call enables instant resizing and
12991             // the second call corrects scroll bars if they
12992             // exist
12993             if(Roo.isIE){
12994                 setTimeout(function(){
12995                     if(el.dom.offsetWidth){
12996                         var b = el.getSize(true);
12997                         child.setSize(b.width+adj[0], b.height+adj[1]);
12998                     }
12999                 }, 10);
13000             }
13001         }
13002     },
13003
13004     // private
13005     snap : function(value, inc, min){
13006         if(!inc || !value) return value;
13007         var newValue = value;
13008         var m = value % inc;
13009         if(m > 0){
13010             if(m > (inc/2)){
13011                 newValue = value + (inc-m);
13012             }else{
13013                 newValue = value - m;
13014             }
13015         }
13016         return Math.max(min, newValue);
13017     },
13018
13019     // private
13020     resizeElement : function(){
13021         var box = this.proxy.getBox();
13022         if(this.updateBox){
13023             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13024         }else{
13025             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13026         }
13027         this.updateChildSize();
13028         if(!this.dynamic){
13029             this.proxy.hide();
13030         }
13031         return box;
13032     },
13033
13034     // private
13035     constrain : function(v, diff, m, mx){
13036         if(v - diff < m){
13037             diff = v - m;
13038         }else if(v - diff > mx){
13039             diff = mx - v;
13040         }
13041         return diff;
13042     },
13043
13044     // private
13045     onMouseMove : function(e){
13046         
13047         if(this.enabled){
13048             try{// try catch so if something goes wrong the user doesn't get hung
13049
13050             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13051                 return;
13052             }
13053
13054             //var curXY = this.startPoint;
13055             var curSize = this.curSize || this.startBox;
13056             var x = this.startBox.x, y = this.startBox.y;
13057             var ox = x, oy = y;
13058             var w = curSize.width, h = curSize.height;
13059             var ow = w, oh = h;
13060             var mw = this.minWidth, mh = this.minHeight;
13061             var mxw = this.maxWidth, mxh = this.maxHeight;
13062             var wi = this.widthIncrement;
13063             var hi = this.heightIncrement;
13064
13065             var eventXY = e.getXY();
13066             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13067             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13068
13069             var pos = this.activeHandle.position;
13070
13071             switch(pos){
13072                 case "east":
13073                     w += diffX;
13074                     w = Math.min(Math.max(mw, w), mxw);
13075                     break;
13076              
13077                 case "south":
13078                     h += diffY;
13079                     h = Math.min(Math.max(mh, h), mxh);
13080                     break;
13081                 case "southeast":
13082                     w += diffX;
13083                     h += diffY;
13084                     w = Math.min(Math.max(mw, w), mxw);
13085                     h = Math.min(Math.max(mh, h), mxh);
13086                     break;
13087                 case "north":
13088                     diffY = this.constrain(h, diffY, mh, mxh);
13089                     y += diffY;
13090                     h -= diffY;
13091                     break;
13092                 case "hdrag":
13093                     
13094                     if (wi) {
13095                         var adiffX = Math.abs(diffX);
13096                         var sub = (adiffX % wi); // how much 
13097                         if (sub > (wi/2)) { // far enough to snap
13098                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13099                         } else {
13100                             // remove difference.. 
13101                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13102                         }
13103                     }
13104                     x += diffX;
13105                     x = Math.max(this.minX, x);
13106                     break;
13107                 case "west":
13108                     diffX = this.constrain(w, diffX, mw, mxw);
13109                     x += diffX;
13110                     w -= diffX;
13111                     break;
13112                 case "northeast":
13113                     w += diffX;
13114                     w = Math.min(Math.max(mw, w), mxw);
13115                     diffY = this.constrain(h, diffY, mh, mxh);
13116                     y += diffY;
13117                     h -= diffY;
13118                     break;
13119                 case "northwest":
13120                     diffX = this.constrain(w, diffX, mw, mxw);
13121                     diffY = this.constrain(h, diffY, mh, mxh);
13122                     y += diffY;
13123                     h -= diffY;
13124                     x += diffX;
13125                     w -= diffX;
13126                     break;
13127                case "southwest":
13128                     diffX = this.constrain(w, diffX, mw, mxw);
13129                     h += diffY;
13130                     h = Math.min(Math.max(mh, h), mxh);
13131                     x += diffX;
13132                     w -= diffX;
13133                     break;
13134             }
13135
13136             var sw = this.snap(w, wi, mw);
13137             var sh = this.snap(h, hi, mh);
13138             if(sw != w || sh != h){
13139                 switch(pos){
13140                     case "northeast":
13141                         y -= sh - h;
13142                     break;
13143                     case "north":
13144                         y -= sh - h;
13145                         break;
13146                     case "southwest":
13147                         x -= sw - w;
13148                     break;
13149                     case "west":
13150                         x -= sw - w;
13151                         break;
13152                     case "northwest":
13153                         x -= sw - w;
13154                         y -= sh - h;
13155                     break;
13156                 }
13157                 w = sw;
13158                 h = sh;
13159             }
13160
13161             if(this.preserveRatio){
13162                 switch(pos){
13163                     case "southeast":
13164                     case "east":
13165                         h = oh * (w/ow);
13166                         h = Math.min(Math.max(mh, h), mxh);
13167                         w = ow * (h/oh);
13168                        break;
13169                     case "south":
13170                         w = ow * (h/oh);
13171                         w = Math.min(Math.max(mw, w), mxw);
13172                         h = oh * (w/ow);
13173                         break;
13174                     case "northeast":
13175                         w = ow * (h/oh);
13176                         w = Math.min(Math.max(mw, w), mxw);
13177                         h = oh * (w/ow);
13178                     break;
13179                     case "north":
13180                         var tw = w;
13181                         w = ow * (h/oh);
13182                         w = Math.min(Math.max(mw, w), mxw);
13183                         h = oh * (w/ow);
13184                         x += (tw - w) / 2;
13185                         break;
13186                     case "southwest":
13187                         h = oh * (w/ow);
13188                         h = Math.min(Math.max(mh, h), mxh);
13189                         var tw = w;
13190                         w = ow * (h/oh);
13191                         x += tw - w;
13192                         break;
13193                     case "west":
13194                         var th = h;
13195                         h = oh * (w/ow);
13196                         h = Math.min(Math.max(mh, h), mxh);
13197                         y += (th - h) / 2;
13198                         var tw = w;
13199                         w = ow * (h/oh);
13200                         x += tw - w;
13201                        break;
13202                     case "northwest":
13203                         var tw = w;
13204                         var th = h;
13205                         h = oh * (w/ow);
13206                         h = Math.min(Math.max(mh, h), mxh);
13207                         w = ow * (h/oh);
13208                         y += th - h;
13209                         x += tw - w;
13210                        break;
13211
13212                 }
13213             }
13214             if (pos == 'hdrag') {
13215                 w = ow;
13216             }
13217             this.proxy.setBounds(x, y, w, h);
13218             if(this.dynamic){
13219                 this.resizeElement();
13220             }
13221             }catch(e){}
13222         }
13223         this.fireEvent("resizing", this, x, y, w, h, e);
13224     },
13225
13226     // private
13227     handleOver : function(){
13228         if(this.enabled){
13229             this.el.addClass("x-resizable-over");
13230         }
13231     },
13232
13233     // private
13234     handleOut : function(){
13235         if(!this.resizing){
13236             this.el.removeClass("x-resizable-over");
13237         }
13238     },
13239
13240     /**
13241      * Returns the element this component is bound to.
13242      * @return {Roo.Element}
13243      */
13244     getEl : function(){
13245         return this.el;
13246     },
13247
13248     /**
13249      * Returns the resizeChild element (or null).
13250      * @return {Roo.Element}
13251      */
13252     getResizeChild : function(){
13253         return this.resizeChild;
13254     },
13255     groupHandler : function()
13256     {
13257         
13258     },
13259     /**
13260      * Destroys this resizable. If the element was wrapped and
13261      * removeEl is not true then the element remains.
13262      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13263      */
13264     destroy : function(removeEl){
13265         this.proxy.remove();
13266         if(this.overlay){
13267             this.overlay.removeAllListeners();
13268             this.overlay.remove();
13269         }
13270         var ps = Roo.Resizable.positions;
13271         for(var k in ps){
13272             if(typeof ps[k] != "function" && this[ps[k]]){
13273                 var h = this[ps[k]];
13274                 h.el.removeAllListeners();
13275                 h.el.remove();
13276             }
13277         }
13278         if(removeEl){
13279             this.el.update("");
13280             this.el.remove();
13281         }
13282     }
13283 });
13284
13285 // private
13286 // hash to map config positions to true positions
13287 Roo.Resizable.positions = {
13288     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13289     hd: "hdrag"
13290 };
13291
13292 // private
13293 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13294     if(!this.tpl){
13295         // only initialize the template if resizable is used
13296         var tpl = Roo.DomHelper.createTemplate(
13297             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13298         );
13299         tpl.compile();
13300         Roo.Resizable.Handle.prototype.tpl = tpl;
13301     }
13302     this.position = pos;
13303     this.rz = rz;
13304     // show north drag fro topdra
13305     var handlepos = pos == 'hdrag' ? 'north' : pos;
13306     
13307     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13308     if (pos == 'hdrag') {
13309         this.el.setStyle('cursor', 'pointer');
13310     }
13311     this.el.unselectable();
13312     if(transparent){
13313         this.el.setOpacity(0);
13314     }
13315     this.el.on("mousedown", this.onMouseDown, this);
13316     if(!disableTrackOver){
13317         this.el.on("mouseover", this.onMouseOver, this);
13318         this.el.on("mouseout", this.onMouseOut, this);
13319     }
13320 };
13321
13322 // private
13323 Roo.Resizable.Handle.prototype = {
13324     afterResize : function(rz){
13325         Roo.log('after?');
13326         // do nothing
13327     },
13328     // private
13329     onMouseDown : function(e){
13330         this.rz.onMouseDown(this, e);
13331     },
13332     // private
13333     onMouseOver : function(e){
13334         this.rz.handleOver(this, e);
13335     },
13336     // private
13337     onMouseOut : function(e){
13338         this.rz.handleOut(this, e);
13339     }
13340 };/*
13341  * Based on:
13342  * Ext JS Library 1.1.1
13343  * Copyright(c) 2006-2007, Ext JS, LLC.
13344  *
13345  * Originally Released Under LGPL - original licence link has changed is not relivant.
13346  *
13347  * Fork - LGPL
13348  * <script type="text/javascript">
13349  */
13350
13351 /**
13352  * @class Roo.Editor
13353  * @extends Roo.Component
13354  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13355  * @constructor
13356  * Create a new Editor
13357  * @param {Roo.form.Field} field The Field object (or descendant)
13358  * @param {Object} config The config object
13359  */
13360 Roo.Editor = function(field, config){
13361     Roo.Editor.superclass.constructor.call(this, config);
13362     this.field = field;
13363     this.addEvents({
13364         /**
13365              * @event beforestartedit
13366              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13367              * false from the handler of this event.
13368              * @param {Editor} this
13369              * @param {Roo.Element} boundEl The underlying element bound to this editor
13370              * @param {Mixed} value The field value being set
13371              */
13372         "beforestartedit" : true,
13373         /**
13374              * @event startedit
13375              * Fires when this editor is displayed
13376              * @param {Roo.Element} boundEl The underlying element bound to this editor
13377              * @param {Mixed} value The starting field value
13378              */
13379         "startedit" : true,
13380         /**
13381              * @event beforecomplete
13382              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13383              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13384              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13385              * event will not fire since no edit actually occurred.
13386              * @param {Editor} this
13387              * @param {Mixed} value The current field value
13388              * @param {Mixed} startValue The original field value
13389              */
13390         "beforecomplete" : true,
13391         /**
13392              * @event complete
13393              * Fires after editing is complete and any changed value has been written to the underlying field.
13394              * @param {Editor} this
13395              * @param {Mixed} value The current field value
13396              * @param {Mixed} startValue The original field value
13397              */
13398         "complete" : true,
13399         /**
13400          * @event specialkey
13401          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13402          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13403          * @param {Roo.form.Field} this
13404          * @param {Roo.EventObject} e The event object
13405          */
13406         "specialkey" : true
13407     });
13408 };
13409
13410 Roo.extend(Roo.Editor, Roo.Component, {
13411     /**
13412      * @cfg {Boolean/String} autosize
13413      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13414      * or "height" to adopt the height only (defaults to false)
13415      */
13416     /**
13417      * @cfg {Boolean} revertInvalid
13418      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13419      * validation fails (defaults to true)
13420      */
13421     /**
13422      * @cfg {Boolean} ignoreNoChange
13423      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13424      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13425      * will never be ignored.
13426      */
13427     /**
13428      * @cfg {Boolean} hideEl
13429      * False to keep the bound element visible while the editor is displayed (defaults to true)
13430      */
13431     /**
13432      * @cfg {Mixed} value
13433      * The data value of the underlying field (defaults to "")
13434      */
13435     value : "",
13436     /**
13437      * @cfg {String} alignment
13438      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13439      */
13440     alignment: "c-c?",
13441     /**
13442      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13443      * for bottom-right shadow (defaults to "frame")
13444      */
13445     shadow : "frame",
13446     /**
13447      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13448      */
13449     constrain : false,
13450     /**
13451      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13452      */
13453     completeOnEnter : false,
13454     /**
13455      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13456      */
13457     cancelOnEsc : false,
13458     /**
13459      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13460      */
13461     updateEl : false,
13462
13463     // private
13464     onRender : function(ct, position){
13465         this.el = new Roo.Layer({
13466             shadow: this.shadow,
13467             cls: "x-editor",
13468             parentEl : ct,
13469             shim : this.shim,
13470             shadowOffset:4,
13471             id: this.id,
13472             constrain: this.constrain
13473         });
13474         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13475         if(this.field.msgTarget != 'title'){
13476             this.field.msgTarget = 'qtip';
13477         }
13478         this.field.render(this.el);
13479         if(Roo.isGecko){
13480             this.field.el.dom.setAttribute('autocomplete', 'off');
13481         }
13482         this.field.on("specialkey", this.onSpecialKey, this);
13483         if(this.swallowKeys){
13484             this.field.el.swallowEvent(['keydown','keypress']);
13485         }
13486         this.field.show();
13487         this.field.on("blur", this.onBlur, this);
13488         if(this.field.grow){
13489             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13490         }
13491     },
13492
13493     onSpecialKey : function(field, e)
13494     {
13495         //Roo.log('editor onSpecialKey');
13496         if(this.completeOnEnter && e.getKey() == e.ENTER){
13497             e.stopEvent();
13498             this.completeEdit();
13499             return;
13500         }
13501         // do not fire special key otherwise it might hide close the editor...
13502         if(e.getKey() == e.ENTER){    
13503             return;
13504         }
13505         if(this.cancelOnEsc && e.getKey() == e.ESC){
13506             this.cancelEdit();
13507             return;
13508         } 
13509         this.fireEvent('specialkey', field, e);
13510     
13511     },
13512
13513     /**
13514      * Starts the editing process and shows the editor.
13515      * @param {String/HTMLElement/Element} el The element to edit
13516      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13517       * to the innerHTML of el.
13518      */
13519     startEdit : function(el, value){
13520         if(this.editing){
13521             this.completeEdit();
13522         }
13523         this.boundEl = Roo.get(el);
13524         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13525         if(!this.rendered){
13526             this.render(this.parentEl || document.body);
13527         }
13528         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13529             return;
13530         }
13531         this.startValue = v;
13532         this.field.setValue(v);
13533         if(this.autoSize){
13534             var sz = this.boundEl.getSize();
13535             switch(this.autoSize){
13536                 case "width":
13537                 this.setSize(sz.width,  "");
13538                 break;
13539                 case "height":
13540                 this.setSize("",  sz.height);
13541                 break;
13542                 default:
13543                 this.setSize(sz.width,  sz.height);
13544             }
13545         }
13546         this.el.alignTo(this.boundEl, this.alignment);
13547         this.editing = true;
13548         if(Roo.QuickTips){
13549             Roo.QuickTips.disable();
13550         }
13551         this.show();
13552     },
13553
13554     /**
13555      * Sets the height and width of this editor.
13556      * @param {Number} width The new width
13557      * @param {Number} height The new height
13558      */
13559     setSize : function(w, h){
13560         this.field.setSize(w, h);
13561         if(this.el){
13562             this.el.sync();
13563         }
13564     },
13565
13566     /**
13567      * Realigns the editor to the bound field based on the current alignment config value.
13568      */
13569     realign : function(){
13570         this.el.alignTo(this.boundEl, this.alignment);
13571     },
13572
13573     /**
13574      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13575      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13576      */
13577     completeEdit : function(remainVisible){
13578         if(!this.editing){
13579             return;
13580         }
13581         var v = this.getValue();
13582         if(this.revertInvalid !== false && !this.field.isValid()){
13583             v = this.startValue;
13584             this.cancelEdit(true);
13585         }
13586         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13587             this.editing = false;
13588             this.hide();
13589             return;
13590         }
13591         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13592             this.editing = false;
13593             if(this.updateEl && this.boundEl){
13594                 this.boundEl.update(v);
13595             }
13596             if(remainVisible !== true){
13597                 this.hide();
13598             }
13599             this.fireEvent("complete", this, v, this.startValue);
13600         }
13601     },
13602
13603     // private
13604     onShow : function(){
13605         this.el.show();
13606         if(this.hideEl !== false){
13607             this.boundEl.hide();
13608         }
13609         this.field.show();
13610         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13611             this.fixIEFocus = true;
13612             this.deferredFocus.defer(50, this);
13613         }else{
13614             this.field.focus();
13615         }
13616         this.fireEvent("startedit", this.boundEl, this.startValue);
13617     },
13618
13619     deferredFocus : function(){
13620         if(this.editing){
13621             this.field.focus();
13622         }
13623     },
13624
13625     /**
13626      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13627      * reverted to the original starting value.
13628      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13629      * cancel (defaults to false)
13630      */
13631     cancelEdit : function(remainVisible){
13632         if(this.editing){
13633             this.setValue(this.startValue);
13634             if(remainVisible !== true){
13635                 this.hide();
13636             }
13637         }
13638     },
13639
13640     // private
13641     onBlur : function(){
13642         if(this.allowBlur !== true && this.editing){
13643             this.completeEdit();
13644         }
13645     },
13646
13647     // private
13648     onHide : function(){
13649         if(this.editing){
13650             this.completeEdit();
13651             return;
13652         }
13653         this.field.blur();
13654         if(this.field.collapse){
13655             this.field.collapse();
13656         }
13657         this.el.hide();
13658         if(this.hideEl !== false){
13659             this.boundEl.show();
13660         }
13661         if(Roo.QuickTips){
13662             Roo.QuickTips.enable();
13663         }
13664     },
13665
13666     /**
13667      * Sets the data value of the editor
13668      * @param {Mixed} value Any valid value supported by the underlying field
13669      */
13670     setValue : function(v){
13671         this.field.setValue(v);
13672     },
13673
13674     /**
13675      * Gets the data value of the editor
13676      * @return {Mixed} The data value
13677      */
13678     getValue : function(){
13679         return this.field.getValue();
13680     }
13681 });/*
13682  * Based on:
13683  * Ext JS Library 1.1.1
13684  * Copyright(c) 2006-2007, Ext JS, LLC.
13685  *
13686  * Originally Released Under LGPL - original licence link has changed is not relivant.
13687  *
13688  * Fork - LGPL
13689  * <script type="text/javascript">
13690  */
13691  
13692 /**
13693  * @class Roo.BasicDialog
13694  * @extends Roo.util.Observable
13695  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13696  * <pre><code>
13697 var dlg = new Roo.BasicDialog("my-dlg", {
13698     height: 200,
13699     width: 300,
13700     minHeight: 100,
13701     minWidth: 150,
13702     modal: true,
13703     proxyDrag: true,
13704     shadow: true
13705 });
13706 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13707 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13708 dlg.addButton('Cancel', dlg.hide, dlg);
13709 dlg.show();
13710 </code></pre>
13711   <b>A Dialog should always be a direct child of the body element.</b>
13712  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13713  * @cfg {String} title Default text to display in the title bar (defaults to null)
13714  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13715  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13716  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13717  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13718  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13719  * (defaults to null with no animation)
13720  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13721  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13722  * property for valid values (defaults to 'all')
13723  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13724  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13725  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13726  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13727  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13728  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13729  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13730  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13731  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13732  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13733  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13734  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13735  * draggable = true (defaults to false)
13736  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13737  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13738  * shadow (defaults to false)
13739  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13740  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13741  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13742  * @cfg {Array} buttons Array of buttons
13743  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13744  * @constructor
13745  * Create a new BasicDialog.
13746  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13747  * @param {Object} config Configuration options
13748  */
13749 Roo.BasicDialog = function(el, config){
13750     this.el = Roo.get(el);
13751     var dh = Roo.DomHelper;
13752     if(!this.el && config && config.autoCreate){
13753         if(typeof config.autoCreate == "object"){
13754             if(!config.autoCreate.id){
13755                 config.autoCreate.id = el;
13756             }
13757             this.el = dh.append(document.body,
13758                         config.autoCreate, true);
13759         }else{
13760             this.el = dh.append(document.body,
13761                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13762         }
13763     }
13764     el = this.el;
13765     el.setDisplayed(true);
13766     el.hide = this.hideAction;
13767     this.id = el.id;
13768     el.addClass("x-dlg");
13769
13770     Roo.apply(this, config);
13771
13772     this.proxy = el.createProxy("x-dlg-proxy");
13773     this.proxy.hide = this.hideAction;
13774     this.proxy.setOpacity(.5);
13775     this.proxy.hide();
13776
13777     if(config.width){
13778         el.setWidth(config.width);
13779     }
13780     if(config.height){
13781         el.setHeight(config.height);
13782     }
13783     this.size = el.getSize();
13784     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13785         this.xy = [config.x,config.y];
13786     }else{
13787         this.xy = el.getCenterXY(true);
13788     }
13789     /** The header element @type Roo.Element */
13790     this.header = el.child("> .x-dlg-hd");
13791     /** The body element @type Roo.Element */
13792     this.body = el.child("> .x-dlg-bd");
13793     /** The footer element @type Roo.Element */
13794     this.footer = el.child("> .x-dlg-ft");
13795
13796     if(!this.header){
13797         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13798     }
13799     if(!this.body){
13800         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13801     }
13802
13803     this.header.unselectable();
13804     if(this.title){
13805         this.header.update(this.title);
13806     }
13807     // this element allows the dialog to be focused for keyboard event
13808     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13809     this.focusEl.swallowEvent("click", true);
13810
13811     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13812
13813     // wrap the body and footer for special rendering
13814     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13815     if(this.footer){
13816         this.bwrap.dom.appendChild(this.footer.dom);
13817     }
13818
13819     this.bg = this.el.createChild({
13820         tag: "div", cls:"x-dlg-bg",
13821         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13822     });
13823     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13824
13825
13826     if(this.autoScroll !== false && !this.autoTabs){
13827         this.body.setStyle("overflow", "auto");
13828     }
13829
13830     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13831
13832     if(this.closable !== false){
13833         this.el.addClass("x-dlg-closable");
13834         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13835         this.close.on("click", this.closeClick, this);
13836         this.close.addClassOnOver("x-dlg-close-over");
13837     }
13838     if(this.collapsible !== false){
13839         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13840         this.collapseBtn.on("click", this.collapseClick, this);
13841         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13842         this.header.on("dblclick", this.collapseClick, this);
13843     }
13844     if(this.resizable !== false){
13845         this.el.addClass("x-dlg-resizable");
13846         this.resizer = new Roo.Resizable(el, {
13847             minWidth: this.minWidth || 80,
13848             minHeight:this.minHeight || 80,
13849             handles: this.resizeHandles || "all",
13850             pinned: true
13851         });
13852         this.resizer.on("beforeresize", this.beforeResize, this);
13853         this.resizer.on("resize", this.onResize, this);
13854     }
13855     if(this.draggable !== false){
13856         el.addClass("x-dlg-draggable");
13857         if (!this.proxyDrag) {
13858             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13859         }
13860         else {
13861             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13862         }
13863         dd.setHandleElId(this.header.id);
13864         dd.endDrag = this.endMove.createDelegate(this);
13865         dd.startDrag = this.startMove.createDelegate(this);
13866         dd.onDrag = this.onDrag.createDelegate(this);
13867         dd.scroll = false;
13868         this.dd = dd;
13869     }
13870     if(this.modal){
13871         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13872         this.mask.enableDisplayMode("block");
13873         this.mask.hide();
13874         this.el.addClass("x-dlg-modal");
13875     }
13876     if(this.shadow){
13877         this.shadow = new Roo.Shadow({
13878             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13879             offset : this.shadowOffset
13880         });
13881     }else{
13882         this.shadowOffset = 0;
13883     }
13884     if(Roo.useShims && this.shim !== false){
13885         this.shim = this.el.createShim();
13886         this.shim.hide = this.hideAction;
13887         this.shim.hide();
13888     }else{
13889         this.shim = false;
13890     }
13891     if(this.autoTabs){
13892         this.initTabs();
13893     }
13894     if (this.buttons) { 
13895         var bts= this.buttons;
13896         this.buttons = [];
13897         Roo.each(bts, function(b) {
13898             this.addButton(b);
13899         }, this);
13900     }
13901     
13902     
13903     this.addEvents({
13904         /**
13905          * @event keydown
13906          * Fires when a key is pressed
13907          * @param {Roo.BasicDialog} this
13908          * @param {Roo.EventObject} e
13909          */
13910         "keydown" : true,
13911         /**
13912          * @event move
13913          * Fires when this dialog is moved by the user.
13914          * @param {Roo.BasicDialog} this
13915          * @param {Number} x The new page X
13916          * @param {Number} y The new page Y
13917          */
13918         "move" : true,
13919         /**
13920          * @event resize
13921          * Fires when this dialog is resized by the user.
13922          * @param {Roo.BasicDialog} this
13923          * @param {Number} width The new width
13924          * @param {Number} height The new height
13925          */
13926         "resize" : true,
13927         /**
13928          * @event beforehide
13929          * Fires before this dialog is hidden.
13930          * @param {Roo.BasicDialog} this
13931          */
13932         "beforehide" : true,
13933         /**
13934          * @event hide
13935          * Fires when this dialog is hidden.
13936          * @param {Roo.BasicDialog} this
13937          */
13938         "hide" : true,
13939         /**
13940          * @event beforeshow
13941          * Fires before this dialog is shown.
13942          * @param {Roo.BasicDialog} this
13943          */
13944         "beforeshow" : true,
13945         /**
13946          * @event show
13947          * Fires when this dialog is shown.
13948          * @param {Roo.BasicDialog} this
13949          */
13950         "show" : true
13951     });
13952     el.on("keydown", this.onKeyDown, this);
13953     el.on("mousedown", this.toFront, this);
13954     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13955     this.el.hide();
13956     Roo.DialogManager.register(this);
13957     Roo.BasicDialog.superclass.constructor.call(this);
13958 };
13959
13960 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13961     shadowOffset: Roo.isIE ? 6 : 5,
13962     minHeight: 80,
13963     minWidth: 200,
13964     minButtonWidth: 75,
13965     defaultButton: null,
13966     buttonAlign: "right",
13967     tabTag: 'div',
13968     firstShow: true,
13969
13970     /**
13971      * Sets the dialog title text
13972      * @param {String} text The title text to display
13973      * @return {Roo.BasicDialog} this
13974      */
13975     setTitle : function(text){
13976         this.header.update(text);
13977         return this;
13978     },
13979
13980     // private
13981     closeClick : function(){
13982         this.hide();
13983     },
13984
13985     // private
13986     collapseClick : function(){
13987         this[this.collapsed ? "expand" : "collapse"]();
13988     },
13989
13990     /**
13991      * Collapses the dialog to its minimized state (only the title bar is visible).
13992      * Equivalent to the user clicking the collapse dialog button.
13993      */
13994     collapse : function(){
13995         if(!this.collapsed){
13996             this.collapsed = true;
13997             this.el.addClass("x-dlg-collapsed");
13998             this.restoreHeight = this.el.getHeight();
13999             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14000         }
14001     },
14002
14003     /**
14004      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14005      * clicking the expand dialog button.
14006      */
14007     expand : function(){
14008         if(this.collapsed){
14009             this.collapsed = false;
14010             this.el.removeClass("x-dlg-collapsed");
14011             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14012         }
14013     },
14014
14015     /**
14016      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14017      * @return {Roo.TabPanel} The tabs component
14018      */
14019     initTabs : function(){
14020         var tabs = this.getTabs();
14021         while(tabs.getTab(0)){
14022             tabs.removeTab(0);
14023         }
14024         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14025             var dom = el.dom;
14026             tabs.addTab(Roo.id(dom), dom.title);
14027             dom.title = "";
14028         });
14029         tabs.activate(0);
14030         return tabs;
14031     },
14032
14033     // private
14034     beforeResize : function(){
14035         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14036     },
14037
14038     // private
14039     onResize : function(){
14040         this.refreshSize();
14041         this.syncBodyHeight();
14042         this.adjustAssets();
14043         this.focus();
14044         this.fireEvent("resize", this, this.size.width, this.size.height);
14045     },
14046
14047     // private
14048     onKeyDown : function(e){
14049         if(this.isVisible()){
14050             this.fireEvent("keydown", this, e);
14051         }
14052     },
14053
14054     /**
14055      * Resizes the dialog.
14056      * @param {Number} width
14057      * @param {Number} height
14058      * @return {Roo.BasicDialog} this
14059      */
14060     resizeTo : function(width, height){
14061         this.el.setSize(width, height);
14062         this.size = {width: width, height: height};
14063         this.syncBodyHeight();
14064         if(this.fixedcenter){
14065             this.center();
14066         }
14067         if(this.isVisible()){
14068             this.constrainXY();
14069             this.adjustAssets();
14070         }
14071         this.fireEvent("resize", this, width, height);
14072         return this;
14073     },
14074
14075
14076     /**
14077      * Resizes the dialog to fit the specified content size.
14078      * @param {Number} width
14079      * @param {Number} height
14080      * @return {Roo.BasicDialog} this
14081      */
14082     setContentSize : function(w, h){
14083         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14084         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14085         //if(!this.el.isBorderBox()){
14086             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14087             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14088         //}
14089         if(this.tabs){
14090             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14091             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14092         }
14093         this.resizeTo(w, h);
14094         return this;
14095     },
14096
14097     /**
14098      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14099      * executed in response to a particular key being pressed while the dialog is active.
14100      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14101      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14102      * @param {Function} fn The function to call
14103      * @param {Object} scope (optional) The scope of the function
14104      * @return {Roo.BasicDialog} this
14105      */
14106     addKeyListener : function(key, fn, scope){
14107         var keyCode, shift, ctrl, alt;
14108         if(typeof key == "object" && !(key instanceof Array)){
14109             keyCode = key["key"];
14110             shift = key["shift"];
14111             ctrl = key["ctrl"];
14112             alt = key["alt"];
14113         }else{
14114             keyCode = key;
14115         }
14116         var handler = function(dlg, e){
14117             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14118                 var k = e.getKey();
14119                 if(keyCode instanceof Array){
14120                     for(var i = 0, len = keyCode.length; i < len; i++){
14121                         if(keyCode[i] == k){
14122                           fn.call(scope || window, dlg, k, e);
14123                           return;
14124                         }
14125                     }
14126                 }else{
14127                     if(k == keyCode){
14128                         fn.call(scope || window, dlg, k, e);
14129                     }
14130                 }
14131             }
14132         };
14133         this.on("keydown", handler);
14134         return this;
14135     },
14136
14137     /**
14138      * Returns the TabPanel component (creates it if it doesn't exist).
14139      * Note: If you wish to simply check for the existence of tabs without creating them,
14140      * check for a null 'tabs' property.
14141      * @return {Roo.TabPanel} The tabs component
14142      */
14143     getTabs : function(){
14144         if(!this.tabs){
14145             this.el.addClass("x-dlg-auto-tabs");
14146             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14147             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14148         }
14149         return this.tabs;
14150     },
14151
14152     /**
14153      * Adds a button to the footer section of the dialog.
14154      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14155      * object or a valid Roo.DomHelper element config
14156      * @param {Function} handler The function called when the button is clicked
14157      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14158      * @return {Roo.Button} The new button
14159      */
14160     addButton : function(config, handler, scope){
14161         var dh = Roo.DomHelper;
14162         if(!this.footer){
14163             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14164         }
14165         if(!this.btnContainer){
14166             var tb = this.footer.createChild({
14167
14168                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14169                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14170             }, null, true);
14171             this.btnContainer = tb.firstChild.firstChild.firstChild;
14172         }
14173         var bconfig = {
14174             handler: handler,
14175             scope: scope,
14176             minWidth: this.minButtonWidth,
14177             hideParent:true
14178         };
14179         if(typeof config == "string"){
14180             bconfig.text = config;
14181         }else{
14182             if(config.tag){
14183                 bconfig.dhconfig = config;
14184             }else{
14185                 Roo.apply(bconfig, config);
14186             }
14187         }
14188         var fc = false;
14189         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14190             bconfig.position = Math.max(0, bconfig.position);
14191             fc = this.btnContainer.childNodes[bconfig.position];
14192         }
14193          
14194         var btn = new Roo.Button(
14195             fc ? 
14196                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14197                 : this.btnContainer.appendChild(document.createElement("td")),
14198             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14199             bconfig
14200         );
14201         this.syncBodyHeight();
14202         if(!this.buttons){
14203             /**
14204              * Array of all the buttons that have been added to this dialog via addButton
14205              * @type Array
14206              */
14207             this.buttons = [];
14208         }
14209         this.buttons.push(btn);
14210         return btn;
14211     },
14212
14213     /**
14214      * Sets the default button to be focused when the dialog is displayed.
14215      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14216      * @return {Roo.BasicDialog} this
14217      */
14218     setDefaultButton : function(btn){
14219         this.defaultButton = btn;
14220         return this;
14221     },
14222
14223     // private
14224     getHeaderFooterHeight : function(safe){
14225         var height = 0;
14226         if(this.header){
14227            height += this.header.getHeight();
14228         }
14229         if(this.footer){
14230            var fm = this.footer.getMargins();
14231             height += (this.footer.getHeight()+fm.top+fm.bottom);
14232         }
14233         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14234         height += this.centerBg.getPadding("tb");
14235         return height;
14236     },
14237
14238     // private
14239     syncBodyHeight : function()
14240     {
14241         var bd = this.body, // the text
14242             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14243             bw = this.bwrap;
14244         var height = this.size.height - this.getHeaderFooterHeight(false);
14245         bd.setHeight(height-bd.getMargins("tb"));
14246         var hh = this.header.getHeight();
14247         var h = this.size.height-hh;
14248         cb.setHeight(h);
14249         
14250         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14251         bw.setHeight(h-cb.getPadding("tb"));
14252         
14253         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14254         bd.setWidth(bw.getWidth(true));
14255         if(this.tabs){
14256             this.tabs.syncHeight();
14257             if(Roo.isIE){
14258                 this.tabs.el.repaint();
14259             }
14260         }
14261     },
14262
14263     /**
14264      * Restores the previous state of the dialog if Roo.state is configured.
14265      * @return {Roo.BasicDialog} this
14266      */
14267     restoreState : function(){
14268         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14269         if(box && box.width){
14270             this.xy = [box.x, box.y];
14271             this.resizeTo(box.width, box.height);
14272         }
14273         return this;
14274     },
14275
14276     // private
14277     beforeShow : function(){
14278         this.expand();
14279         if(this.fixedcenter){
14280             this.xy = this.el.getCenterXY(true);
14281         }
14282         if(this.modal){
14283             Roo.get(document.body).addClass("x-body-masked");
14284             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14285             this.mask.show();
14286         }
14287         this.constrainXY();
14288     },
14289
14290     // private
14291     animShow : function(){
14292         var b = Roo.get(this.animateTarget).getBox();
14293         this.proxy.setSize(b.width, b.height);
14294         this.proxy.setLocation(b.x, b.y);
14295         this.proxy.show();
14296         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14297                     true, .35, this.showEl.createDelegate(this));
14298     },
14299
14300     /**
14301      * Shows the dialog.
14302      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14303      * @return {Roo.BasicDialog} this
14304      */
14305     show : function(animateTarget){
14306         if (this.fireEvent("beforeshow", this) === false){
14307             return;
14308         }
14309         if(this.syncHeightBeforeShow){
14310             this.syncBodyHeight();
14311         }else if(this.firstShow){
14312             this.firstShow = false;
14313             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14314         }
14315         this.animateTarget = animateTarget || this.animateTarget;
14316         if(!this.el.isVisible()){
14317             this.beforeShow();
14318             if(this.animateTarget && Roo.get(this.animateTarget)){
14319                 this.animShow();
14320             }else{
14321                 this.showEl();
14322             }
14323         }
14324         return this;
14325     },
14326
14327     // private
14328     showEl : function(){
14329         this.proxy.hide();
14330         this.el.setXY(this.xy);
14331         this.el.show();
14332         this.adjustAssets(true);
14333         this.toFront();
14334         this.focus();
14335         // IE peekaboo bug - fix found by Dave Fenwick
14336         if(Roo.isIE){
14337             this.el.repaint();
14338         }
14339         this.fireEvent("show", this);
14340     },
14341
14342     /**
14343      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14344      * dialog itself will receive focus.
14345      */
14346     focus : function(){
14347         if(this.defaultButton){
14348             this.defaultButton.focus();
14349         }else{
14350             this.focusEl.focus();
14351         }
14352     },
14353
14354     // private
14355     constrainXY : function(){
14356         if(this.constraintoviewport !== false){
14357             if(!this.viewSize){
14358                 if(this.container){
14359                     var s = this.container.getSize();
14360                     this.viewSize = [s.width, s.height];
14361                 }else{
14362                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14363                 }
14364             }
14365             var s = Roo.get(this.container||document).getScroll();
14366
14367             var x = this.xy[0], y = this.xy[1];
14368             var w = this.size.width, h = this.size.height;
14369             var vw = this.viewSize[0], vh = this.viewSize[1];
14370             // only move it if it needs it
14371             var moved = false;
14372             // first validate right/bottom
14373             if(x + w > vw+s.left){
14374                 x = vw - w;
14375                 moved = true;
14376             }
14377             if(y + h > vh+s.top){
14378                 y = vh - h;
14379                 moved = true;
14380             }
14381             // then make sure top/left isn't negative
14382             if(x < s.left){
14383                 x = s.left;
14384                 moved = true;
14385             }
14386             if(y < s.top){
14387                 y = s.top;
14388                 moved = true;
14389             }
14390             if(moved){
14391                 // cache xy
14392                 this.xy = [x, y];
14393                 if(this.isVisible()){
14394                     this.el.setLocation(x, y);
14395                     this.adjustAssets();
14396                 }
14397             }
14398         }
14399     },
14400
14401     // private
14402     onDrag : function(){
14403         if(!this.proxyDrag){
14404             this.xy = this.el.getXY();
14405             this.adjustAssets();
14406         }
14407     },
14408
14409     // private
14410     adjustAssets : function(doShow){
14411         var x = this.xy[0], y = this.xy[1];
14412         var w = this.size.width, h = this.size.height;
14413         if(doShow === true){
14414             if(this.shadow){
14415                 this.shadow.show(this.el);
14416             }
14417             if(this.shim){
14418                 this.shim.show();
14419             }
14420         }
14421         if(this.shadow && this.shadow.isVisible()){
14422             this.shadow.show(this.el);
14423         }
14424         if(this.shim && this.shim.isVisible()){
14425             this.shim.setBounds(x, y, w, h);
14426         }
14427     },
14428
14429     // private
14430     adjustViewport : function(w, h){
14431         if(!w || !h){
14432             w = Roo.lib.Dom.getViewWidth();
14433             h = Roo.lib.Dom.getViewHeight();
14434         }
14435         // cache the size
14436         this.viewSize = [w, h];
14437         if(this.modal && this.mask.isVisible()){
14438             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14439             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14440         }
14441         if(this.isVisible()){
14442             this.constrainXY();
14443         }
14444     },
14445
14446     /**
14447      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14448      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14449      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14450      */
14451     destroy : function(removeEl){
14452         if(this.isVisible()){
14453             this.animateTarget = null;
14454             this.hide();
14455         }
14456         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14457         if(this.tabs){
14458             this.tabs.destroy(removeEl);
14459         }
14460         Roo.destroy(
14461              this.shim,
14462              this.proxy,
14463              this.resizer,
14464              this.close,
14465              this.mask
14466         );
14467         if(this.dd){
14468             this.dd.unreg();
14469         }
14470         if(this.buttons){
14471            for(var i = 0, len = this.buttons.length; i < len; i++){
14472                this.buttons[i].destroy();
14473            }
14474         }
14475         this.el.removeAllListeners();
14476         if(removeEl === true){
14477             this.el.update("");
14478             this.el.remove();
14479         }
14480         Roo.DialogManager.unregister(this);
14481     },
14482
14483     // private
14484     startMove : function(){
14485         if(this.proxyDrag){
14486             this.proxy.show();
14487         }
14488         if(this.constraintoviewport !== false){
14489             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14490         }
14491     },
14492
14493     // private
14494     endMove : function(){
14495         if(!this.proxyDrag){
14496             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14497         }else{
14498             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14499             this.proxy.hide();
14500         }
14501         this.refreshSize();
14502         this.adjustAssets();
14503         this.focus();
14504         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14505     },
14506
14507     /**
14508      * Brings this dialog to the front of any other visible dialogs
14509      * @return {Roo.BasicDialog} this
14510      */
14511     toFront : function(){
14512         Roo.DialogManager.bringToFront(this);
14513         return this;
14514     },
14515
14516     /**
14517      * Sends this dialog to the back (under) of any other visible dialogs
14518      * @return {Roo.BasicDialog} this
14519      */
14520     toBack : function(){
14521         Roo.DialogManager.sendToBack(this);
14522         return this;
14523     },
14524
14525     /**
14526      * Centers this dialog in the viewport
14527      * @return {Roo.BasicDialog} this
14528      */
14529     center : function(){
14530         var xy = this.el.getCenterXY(true);
14531         this.moveTo(xy[0], xy[1]);
14532         return this;
14533     },
14534
14535     /**
14536      * Moves the dialog's top-left corner to the specified point
14537      * @param {Number} x
14538      * @param {Number} y
14539      * @return {Roo.BasicDialog} this
14540      */
14541     moveTo : function(x, y){
14542         this.xy = [x,y];
14543         if(this.isVisible()){
14544             this.el.setXY(this.xy);
14545             this.adjustAssets();
14546         }
14547         return this;
14548     },
14549
14550     /**
14551      * Aligns the dialog to the specified element
14552      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14553      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14554      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14555      * @return {Roo.BasicDialog} this
14556      */
14557     alignTo : function(element, position, offsets){
14558         this.xy = this.el.getAlignToXY(element, position, offsets);
14559         if(this.isVisible()){
14560             this.el.setXY(this.xy);
14561             this.adjustAssets();
14562         }
14563         return this;
14564     },
14565
14566     /**
14567      * Anchors an element to another element and realigns it when the window is resized.
14568      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14569      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14570      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14571      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14572      * is a number, it is used as the buffer delay (defaults to 50ms).
14573      * @return {Roo.BasicDialog} this
14574      */
14575     anchorTo : function(el, alignment, offsets, monitorScroll){
14576         var action = function(){
14577             this.alignTo(el, alignment, offsets);
14578         };
14579         Roo.EventManager.onWindowResize(action, this);
14580         var tm = typeof monitorScroll;
14581         if(tm != 'undefined'){
14582             Roo.EventManager.on(window, 'scroll', action, this,
14583                 {buffer: tm == 'number' ? monitorScroll : 50});
14584         }
14585         action.call(this);
14586         return this;
14587     },
14588
14589     /**
14590      * Returns true if the dialog is visible
14591      * @return {Boolean}
14592      */
14593     isVisible : function(){
14594         return this.el.isVisible();
14595     },
14596
14597     // private
14598     animHide : function(callback){
14599         var b = Roo.get(this.animateTarget).getBox();
14600         this.proxy.show();
14601         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14602         this.el.hide();
14603         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14604                     this.hideEl.createDelegate(this, [callback]));
14605     },
14606
14607     /**
14608      * Hides the dialog.
14609      * @param {Function} callback (optional) Function to call when the dialog is hidden
14610      * @return {Roo.BasicDialog} this
14611      */
14612     hide : function(callback){
14613         if (this.fireEvent("beforehide", this) === false){
14614             return;
14615         }
14616         if(this.shadow){
14617             this.shadow.hide();
14618         }
14619         if(this.shim) {
14620           this.shim.hide();
14621         }
14622         // sometimes animateTarget seems to get set.. causing problems...
14623         // this just double checks..
14624         if(this.animateTarget && Roo.get(this.animateTarget)) {
14625            this.animHide(callback);
14626         }else{
14627             this.el.hide();
14628             this.hideEl(callback);
14629         }
14630         return this;
14631     },
14632
14633     // private
14634     hideEl : function(callback){
14635         this.proxy.hide();
14636         if(this.modal){
14637             this.mask.hide();
14638             Roo.get(document.body).removeClass("x-body-masked");
14639         }
14640         this.fireEvent("hide", this);
14641         if(typeof callback == "function"){
14642             callback();
14643         }
14644     },
14645
14646     // private
14647     hideAction : function(){
14648         this.setLeft("-10000px");
14649         this.setTop("-10000px");
14650         this.setStyle("visibility", "hidden");
14651     },
14652
14653     // private
14654     refreshSize : function(){
14655         this.size = this.el.getSize();
14656         this.xy = this.el.getXY();
14657         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14658     },
14659
14660     // private
14661     // z-index is managed by the DialogManager and may be overwritten at any time
14662     setZIndex : function(index){
14663         if(this.modal){
14664             this.mask.setStyle("z-index", index);
14665         }
14666         if(this.shim){
14667             this.shim.setStyle("z-index", ++index);
14668         }
14669         if(this.shadow){
14670             this.shadow.setZIndex(++index);
14671         }
14672         this.el.setStyle("z-index", ++index);
14673         if(this.proxy){
14674             this.proxy.setStyle("z-index", ++index);
14675         }
14676         if(this.resizer){
14677             this.resizer.proxy.setStyle("z-index", ++index);
14678         }
14679
14680         this.lastZIndex = index;
14681     },
14682
14683     /**
14684      * Returns the element for this dialog
14685      * @return {Roo.Element} The underlying dialog Element
14686      */
14687     getEl : function(){
14688         return this.el;
14689     }
14690 });
14691
14692 /**
14693  * @class Roo.DialogManager
14694  * Provides global access to BasicDialogs that have been created and
14695  * support for z-indexing (layering) multiple open dialogs.
14696  */
14697 Roo.DialogManager = function(){
14698     var list = {};
14699     var accessList = [];
14700     var front = null;
14701
14702     // private
14703     var sortDialogs = function(d1, d2){
14704         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14705     };
14706
14707     // private
14708     var orderDialogs = function(){
14709         accessList.sort(sortDialogs);
14710         var seed = Roo.DialogManager.zseed;
14711         for(var i = 0, len = accessList.length; i < len; i++){
14712             var dlg = accessList[i];
14713             if(dlg){
14714                 dlg.setZIndex(seed + (i*10));
14715             }
14716         }
14717     };
14718
14719     return {
14720         /**
14721          * The starting z-index for BasicDialogs (defaults to 9000)
14722          * @type Number The z-index value
14723          */
14724         zseed : 9000,
14725
14726         // private
14727         register : function(dlg){
14728             list[dlg.id] = dlg;
14729             accessList.push(dlg);
14730         },
14731
14732         // private
14733         unregister : function(dlg){
14734             delete list[dlg.id];
14735             var i=0;
14736             var len=0;
14737             if(!accessList.indexOf){
14738                 for(  i = 0, len = accessList.length; i < len; i++){
14739                     if(accessList[i] == dlg){
14740                         accessList.splice(i, 1);
14741                         return;
14742                     }
14743                 }
14744             }else{
14745                  i = accessList.indexOf(dlg);
14746                 if(i != -1){
14747                     accessList.splice(i, 1);
14748                 }
14749             }
14750         },
14751
14752         /**
14753          * Gets a registered dialog by id
14754          * @param {String/Object} id The id of the dialog or a dialog
14755          * @return {Roo.BasicDialog} this
14756          */
14757         get : function(id){
14758             return typeof id == "object" ? id : list[id];
14759         },
14760
14761         /**
14762          * Brings the specified dialog to the front
14763          * @param {String/Object} dlg The id of the dialog or a dialog
14764          * @return {Roo.BasicDialog} this
14765          */
14766         bringToFront : function(dlg){
14767             dlg = this.get(dlg);
14768             if(dlg != front){
14769                 front = dlg;
14770                 dlg._lastAccess = new Date().getTime();
14771                 orderDialogs();
14772             }
14773             return dlg;
14774         },
14775
14776         /**
14777          * Sends the specified dialog to the back
14778          * @param {String/Object} dlg The id of the dialog or a dialog
14779          * @return {Roo.BasicDialog} this
14780          */
14781         sendToBack : function(dlg){
14782             dlg = this.get(dlg);
14783             dlg._lastAccess = -(new Date().getTime());
14784             orderDialogs();
14785             return dlg;
14786         },
14787
14788         /**
14789          * Hides all dialogs
14790          */
14791         hideAll : function(){
14792             for(var id in list){
14793                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14794                     list[id].hide();
14795                 }
14796             }
14797         }
14798     };
14799 }();
14800
14801 /**
14802  * @class Roo.LayoutDialog
14803  * @extends Roo.BasicDialog
14804  * Dialog which provides adjustments for working with a layout in a Dialog.
14805  * Add your necessary layout config options to the dialog's config.<br>
14806  * Example usage (including a nested layout):
14807  * <pre><code>
14808 if(!dialog){
14809     dialog = new Roo.LayoutDialog("download-dlg", {
14810         modal: true,
14811         width:600,
14812         height:450,
14813         shadow:true,
14814         minWidth:500,
14815         minHeight:350,
14816         autoTabs:true,
14817         proxyDrag:true,
14818         // layout config merges with the dialog config
14819         center:{
14820             tabPosition: "top",
14821             alwaysShowTabs: true
14822         }
14823     });
14824     dialog.addKeyListener(27, dialog.hide, dialog);
14825     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14826     dialog.addButton("Build It!", this.getDownload, this);
14827
14828     // we can even add nested layouts
14829     var innerLayout = new Roo.BorderLayout("dl-inner", {
14830         east: {
14831             initialSize: 200,
14832             autoScroll:true,
14833             split:true
14834         },
14835         center: {
14836             autoScroll:true
14837         }
14838     });
14839     innerLayout.beginUpdate();
14840     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14841     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14842     innerLayout.endUpdate(true);
14843
14844     var layout = dialog.getLayout();
14845     layout.beginUpdate();
14846     layout.add("center", new Roo.ContentPanel("standard-panel",
14847                         {title: "Download the Source", fitToFrame:true}));
14848     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14849                {title: "Build your own roo.js"}));
14850     layout.getRegion("center").showPanel(sp);
14851     layout.endUpdate();
14852 }
14853 </code></pre>
14854     * @constructor
14855     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14856     * @param {Object} config configuration options
14857   */
14858 Roo.LayoutDialog = function(el, cfg){
14859     
14860     var config=  cfg;
14861     if (typeof(cfg) == 'undefined') {
14862         config = Roo.apply({}, el);
14863         // not sure why we use documentElement here.. - it should always be body.
14864         // IE7 borks horribly if we use documentElement.
14865         // webkit also does not like documentElement - it creates a body element...
14866         el = Roo.get( document.body || document.documentElement ).createChild();
14867         //config.autoCreate = true;
14868     }
14869     
14870     
14871     config.autoTabs = false;
14872     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14873     this.body.setStyle({overflow:"hidden", position:"relative"});
14874     this.layout = new Roo.BorderLayout(this.body.dom, config);
14875     this.layout.monitorWindowResize = false;
14876     this.el.addClass("x-dlg-auto-layout");
14877     // fix case when center region overwrites center function
14878     this.center = Roo.BasicDialog.prototype.center;
14879     this.on("show", this.layout.layout, this.layout, true);
14880     if (config.items) {
14881         var xitems = config.items;
14882         delete config.items;
14883         Roo.each(xitems, this.addxtype, this);
14884     }
14885     
14886     
14887 };
14888 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14889     /**
14890      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14891      * @deprecated
14892      */
14893     endUpdate : function(){
14894         this.layout.endUpdate();
14895     },
14896
14897     /**
14898      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14899      *  @deprecated
14900      */
14901     beginUpdate : function(){
14902         this.layout.beginUpdate();
14903     },
14904
14905     /**
14906      * Get the BorderLayout for this dialog
14907      * @return {Roo.BorderLayout}
14908      */
14909     getLayout : function(){
14910         return this.layout;
14911     },
14912
14913     showEl : function(){
14914         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14915         if(Roo.isIE7){
14916             this.layout.layout();
14917         }
14918     },
14919
14920     // private
14921     // Use the syncHeightBeforeShow config option to control this automatically
14922     syncBodyHeight : function(){
14923         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14924         if(this.layout){this.layout.layout();}
14925     },
14926     
14927       /**
14928      * Add an xtype element (actually adds to the layout.)
14929      * @return {Object} xdata xtype object data.
14930      */
14931     
14932     addxtype : function(c) {
14933         return this.layout.addxtype(c);
14934     }
14935 });/*
14936  * Based on:
14937  * Ext JS Library 1.1.1
14938  * Copyright(c) 2006-2007, Ext JS, LLC.
14939  *
14940  * Originally Released Under LGPL - original licence link has changed is not relivant.
14941  *
14942  * Fork - LGPL
14943  * <script type="text/javascript">
14944  */
14945  
14946 /**
14947  * @class Roo.MessageBox
14948  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14949  * Example usage:
14950  *<pre><code>
14951 // Basic alert:
14952 Roo.Msg.alert('Status', 'Changes saved successfully.');
14953
14954 // Prompt for user data:
14955 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14956     if (btn == 'ok'){
14957         // process text value...
14958     }
14959 });
14960
14961 // Show a dialog using config options:
14962 Roo.Msg.show({
14963    title:'Save Changes?',
14964    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
14965    buttons: Roo.Msg.YESNOCANCEL,
14966    fn: processResult,
14967    animEl: 'elId'
14968 });
14969 </code></pre>
14970  * @singleton
14971  */
14972 Roo.MessageBox = function(){
14973     var dlg, opt, mask, waitTimer;
14974     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
14975     var buttons, activeTextEl, bwidth;
14976
14977     // private
14978     var handleButton = function(button){
14979         dlg.hide();
14980         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
14981     };
14982
14983     // private
14984     var handleHide = function(){
14985         if(opt && opt.cls){
14986             dlg.el.removeClass(opt.cls);
14987         }
14988         if(waitTimer){
14989             Roo.TaskMgr.stop(waitTimer);
14990             waitTimer = null;
14991         }
14992     };
14993
14994     // private
14995     var updateButtons = function(b){
14996         var width = 0;
14997         if(!b){
14998             buttons["ok"].hide();
14999             buttons["cancel"].hide();
15000             buttons["yes"].hide();
15001             buttons["no"].hide();
15002             dlg.footer.dom.style.display = 'none';
15003             return width;
15004         }
15005         dlg.footer.dom.style.display = '';
15006         for(var k in buttons){
15007             if(typeof buttons[k] != "function"){
15008                 if(b[k]){
15009                     buttons[k].show();
15010                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15011                     width += buttons[k].el.getWidth()+15;
15012                 }else{
15013                     buttons[k].hide();
15014                 }
15015             }
15016         }
15017         return width;
15018     };
15019
15020     // private
15021     var handleEsc = function(d, k, e){
15022         if(opt && opt.closable !== false){
15023             dlg.hide();
15024         }
15025         if(e){
15026             e.stopEvent();
15027         }
15028     };
15029
15030     return {
15031         /**
15032          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15033          * @return {Roo.BasicDialog} The BasicDialog element
15034          */
15035         getDialog : function(){
15036            if(!dlg){
15037                 dlg = new Roo.BasicDialog("x-msg-box", {
15038                     autoCreate : true,
15039                     shadow: true,
15040                     draggable: true,
15041                     resizable:false,
15042                     constraintoviewport:false,
15043                     fixedcenter:true,
15044                     collapsible : false,
15045                     shim:true,
15046                     modal: true,
15047                     width:400, height:100,
15048                     buttonAlign:"center",
15049                     closeClick : function(){
15050                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15051                             handleButton("no");
15052                         }else{
15053                             handleButton("cancel");
15054                         }
15055                     }
15056                 });
15057                 dlg.on("hide", handleHide);
15058                 mask = dlg.mask;
15059                 dlg.addKeyListener(27, handleEsc);
15060                 buttons = {};
15061                 var bt = this.buttonText;
15062                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15063                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15064                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15065                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15066                 bodyEl = dlg.body.createChild({
15067
15068                     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>'
15069                 });
15070                 msgEl = bodyEl.dom.firstChild;
15071                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15072                 textboxEl.enableDisplayMode();
15073                 textboxEl.addKeyListener([10,13], function(){
15074                     if(dlg.isVisible() && opt && opt.buttons){
15075                         if(opt.buttons.ok){
15076                             handleButton("ok");
15077                         }else if(opt.buttons.yes){
15078                             handleButton("yes");
15079                         }
15080                     }
15081                 });
15082                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15083                 textareaEl.enableDisplayMode();
15084                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15085                 progressEl.enableDisplayMode();
15086                 var pf = progressEl.dom.firstChild;
15087                 if (pf) {
15088                     pp = Roo.get(pf.firstChild);
15089                     pp.setHeight(pf.offsetHeight);
15090                 }
15091                 
15092             }
15093             return dlg;
15094         },
15095
15096         /**
15097          * Updates the message box body text
15098          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15099          * the XHTML-compliant non-breaking space character '&amp;#160;')
15100          * @return {Roo.MessageBox} This message box
15101          */
15102         updateText : function(text){
15103             if(!dlg.isVisible() && !opt.width){
15104                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15105             }
15106             msgEl.innerHTML = text || '&#160;';
15107       
15108             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15109             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15110             var w = Math.max(
15111                     Math.min(opt.width || cw , this.maxWidth), 
15112                     Math.max(opt.minWidth || this.minWidth, bwidth)
15113             );
15114             if(opt.prompt){
15115                 activeTextEl.setWidth(w);
15116             }
15117             if(dlg.isVisible()){
15118                 dlg.fixedcenter = false;
15119             }
15120             // to big, make it scroll. = But as usual stupid IE does not support
15121             // !important..
15122             
15123             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15124                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15125                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15126             } else {
15127                 bodyEl.dom.style.height = '';
15128                 bodyEl.dom.style.overflowY = '';
15129             }
15130             if (cw > w) {
15131                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15132             } else {
15133                 bodyEl.dom.style.overflowX = '';
15134             }
15135             
15136             dlg.setContentSize(w, bodyEl.getHeight());
15137             if(dlg.isVisible()){
15138                 dlg.fixedcenter = true;
15139             }
15140             return this;
15141         },
15142
15143         /**
15144          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15145          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15146          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15147          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15148          * @return {Roo.MessageBox} This message box
15149          */
15150         updateProgress : function(value, text){
15151             if(text){
15152                 this.updateText(text);
15153             }
15154             if (pp) { // weird bug on my firefox - for some reason this is not defined
15155                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15156             }
15157             return this;
15158         },        
15159
15160         /**
15161          * Returns true if the message box is currently displayed
15162          * @return {Boolean} True if the message box is visible, else false
15163          */
15164         isVisible : function(){
15165             return dlg && dlg.isVisible();  
15166         },
15167
15168         /**
15169          * Hides the message box if it is displayed
15170          */
15171         hide : function(){
15172             if(this.isVisible()){
15173                 dlg.hide();
15174             }  
15175         },
15176
15177         /**
15178          * Displays a new message box, or reinitializes an existing message box, based on the config options
15179          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15180          * The following config object properties are supported:
15181          * <pre>
15182 Property    Type             Description
15183 ----------  ---------------  ------------------------------------------------------------------------------------
15184 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15185                                    closes (defaults to undefined)
15186 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15187                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15188 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15189                                    progress and wait dialogs will ignore this property and always hide the
15190                                    close button as they can only be closed programmatically.
15191 cls               String           A custom CSS class to apply to the message box element
15192 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15193                                    displayed (defaults to 75)
15194 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15195                                    function will be btn (the name of the button that was clicked, if applicable,
15196                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15197                                    Progress and wait dialogs will ignore this option since they do not respond to
15198                                    user actions and can only be closed programmatically, so any required function
15199                                    should be called by the same code after it closes the dialog.
15200 icon              String           A CSS class that provides a background image to be used as an icon for
15201                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15202 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15203 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15204 modal             Boolean          False to allow user interaction with the page while the message box is
15205                                    displayed (defaults to true)
15206 msg               String           A string that will replace the existing message box body text (defaults
15207                                    to the XHTML-compliant non-breaking space character '&#160;')
15208 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15209 progress          Boolean          True to display a progress bar (defaults to false)
15210 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15211 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15212 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15213 title             String           The title text
15214 value             String           The string value to set into the active textbox element if displayed
15215 wait              Boolean          True to display a progress bar (defaults to false)
15216 width             Number           The width of the dialog in pixels
15217 </pre>
15218          *
15219          * Example usage:
15220          * <pre><code>
15221 Roo.Msg.show({
15222    title: 'Address',
15223    msg: 'Please enter your address:',
15224    width: 300,
15225    buttons: Roo.MessageBox.OKCANCEL,
15226    multiline: true,
15227    fn: saveAddress,
15228    animEl: 'addAddressBtn'
15229 });
15230 </code></pre>
15231          * @param {Object} config Configuration options
15232          * @return {Roo.MessageBox} This message box
15233          */
15234         show : function(options)
15235         {
15236             
15237             // this causes nightmares if you show one dialog after another
15238             // especially on callbacks..
15239              
15240             if(this.isVisible()){
15241                 
15242                 this.hide();
15243                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15244                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15245                 Roo.log("New Dialog Message:" +  options.msg )
15246                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15247                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15248                 
15249             }
15250             var d = this.getDialog();
15251             opt = options;
15252             d.setTitle(opt.title || "&#160;");
15253             d.close.setDisplayed(opt.closable !== false);
15254             activeTextEl = textboxEl;
15255             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15256             if(opt.prompt){
15257                 if(opt.multiline){
15258                     textboxEl.hide();
15259                     textareaEl.show();
15260                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15261                         opt.multiline : this.defaultTextHeight);
15262                     activeTextEl = textareaEl;
15263                 }else{
15264                     textboxEl.show();
15265                     textareaEl.hide();
15266                 }
15267             }else{
15268                 textboxEl.hide();
15269                 textareaEl.hide();
15270             }
15271             progressEl.setDisplayed(opt.progress === true);
15272             this.updateProgress(0);
15273             activeTextEl.dom.value = opt.value || "";
15274             if(opt.prompt){
15275                 dlg.setDefaultButton(activeTextEl);
15276             }else{
15277                 var bs = opt.buttons;
15278                 var db = null;
15279                 if(bs && bs.ok){
15280                     db = buttons["ok"];
15281                 }else if(bs && bs.yes){
15282                     db = buttons["yes"];
15283                 }
15284                 dlg.setDefaultButton(db);
15285             }
15286             bwidth = updateButtons(opt.buttons);
15287             this.updateText(opt.msg);
15288             if(opt.cls){
15289                 d.el.addClass(opt.cls);
15290             }
15291             d.proxyDrag = opt.proxyDrag === true;
15292             d.modal = opt.modal !== false;
15293             d.mask = opt.modal !== false ? mask : false;
15294             if(!d.isVisible()){
15295                 // force it to the end of the z-index stack so it gets a cursor in FF
15296                 document.body.appendChild(dlg.el.dom);
15297                 d.animateTarget = null;
15298                 d.show(options.animEl);
15299             }
15300             return this;
15301         },
15302
15303         /**
15304          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15305          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15306          * and closing the message box when the process is complete.
15307          * @param {String} title The title bar text
15308          * @param {String} msg The message box body text
15309          * @return {Roo.MessageBox} This message box
15310          */
15311         progress : function(title, msg){
15312             this.show({
15313                 title : title,
15314                 msg : msg,
15315                 buttons: false,
15316                 progress:true,
15317                 closable:false,
15318                 minWidth: this.minProgressWidth,
15319                 modal : true
15320             });
15321             return this;
15322         },
15323
15324         /**
15325          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15326          * If a callback function is passed it will be called after the user clicks the button, and the
15327          * id of the button that was clicked will be passed as the only parameter to the callback
15328          * (could also be the top-right close button).
15329          * @param {String} title The title bar text
15330          * @param {String} msg The message box body text
15331          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15332          * @param {Object} scope (optional) The scope of the callback function
15333          * @return {Roo.MessageBox} This message box
15334          */
15335         alert : function(title, msg, fn, scope){
15336             this.show({
15337                 title : title,
15338                 msg : msg,
15339                 buttons: this.OK,
15340                 fn: fn,
15341                 scope : scope,
15342                 modal : true
15343             });
15344             return this;
15345         },
15346
15347         /**
15348          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15349          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15350          * You are responsible for closing the message box when the process is complete.
15351          * @param {String} msg The message box body text
15352          * @param {String} title (optional) The title bar text
15353          * @return {Roo.MessageBox} This message box
15354          */
15355         wait : function(msg, title){
15356             this.show({
15357                 title : title,
15358                 msg : msg,
15359                 buttons: false,
15360                 closable:false,
15361                 progress:true,
15362                 modal:true,
15363                 width:300,
15364                 wait:true
15365             });
15366             waitTimer = Roo.TaskMgr.start({
15367                 run: function(i){
15368                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15369                 },
15370                 interval: 1000
15371             });
15372             return this;
15373         },
15374
15375         /**
15376          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15377          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15378          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15379          * @param {String} title The title bar text
15380          * @param {String} msg The message box body text
15381          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15382          * @param {Object} scope (optional) The scope of the callback function
15383          * @return {Roo.MessageBox} This message box
15384          */
15385         confirm : function(title, msg, fn, scope){
15386             this.show({
15387                 title : title,
15388                 msg : msg,
15389                 buttons: this.YESNO,
15390                 fn: fn,
15391                 scope : scope,
15392                 modal : true
15393             });
15394             return this;
15395         },
15396
15397         /**
15398          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15399          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15400          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15401          * (could also be the top-right close button) and the text that was entered will be passed as the two
15402          * parameters to the callback.
15403          * @param {String} title The title bar text
15404          * @param {String} msg The message box body text
15405          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15406          * @param {Object} scope (optional) The scope of the callback function
15407          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15408          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15409          * @return {Roo.MessageBox} This message box
15410          */
15411         prompt : function(title, msg, fn, scope, multiline){
15412             this.show({
15413                 title : title,
15414                 msg : msg,
15415                 buttons: this.OKCANCEL,
15416                 fn: fn,
15417                 minWidth:250,
15418                 scope : scope,
15419                 prompt:true,
15420                 multiline: multiline,
15421                 modal : true
15422             });
15423             return this;
15424         },
15425
15426         /**
15427          * Button config that displays a single OK button
15428          * @type Object
15429          */
15430         OK : {ok:true},
15431         /**
15432          * Button config that displays Yes and No buttons
15433          * @type Object
15434          */
15435         YESNO : {yes:true, no:true},
15436         /**
15437          * Button config that displays OK and Cancel buttons
15438          * @type Object
15439          */
15440         OKCANCEL : {ok:true, cancel:true},
15441         /**
15442          * Button config that displays Yes, No and Cancel buttons
15443          * @type Object
15444          */
15445         YESNOCANCEL : {yes:true, no:true, cancel:true},
15446
15447         /**
15448          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15449          * @type Number
15450          */
15451         defaultTextHeight : 75,
15452         /**
15453          * The maximum width in pixels of the message box (defaults to 600)
15454          * @type Number
15455          */
15456         maxWidth : 600,
15457         /**
15458          * The minimum width in pixels of the message box (defaults to 100)
15459          * @type Number
15460          */
15461         minWidth : 100,
15462         /**
15463          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15464          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15465          * @type Number
15466          */
15467         minProgressWidth : 250,
15468         /**
15469          * An object containing the default button text strings that can be overriden for localized language support.
15470          * Supported properties are: ok, cancel, yes and no.
15471          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15472          * @type Object
15473          */
15474         buttonText : {
15475             ok : "OK",
15476             cancel : "Cancel",
15477             yes : "Yes",
15478             no : "No"
15479         }
15480     };
15481 }();
15482
15483 /**
15484  * Shorthand for {@link Roo.MessageBox}
15485  */
15486 Roo.Msg = Roo.MessageBox;/*
15487  * Based on:
15488  * Ext JS Library 1.1.1
15489  * Copyright(c) 2006-2007, Ext JS, LLC.
15490  *
15491  * Originally Released Under LGPL - original licence link has changed is not relivant.
15492  *
15493  * Fork - LGPL
15494  * <script type="text/javascript">
15495  */
15496 /**
15497  * @class Roo.QuickTips
15498  * Provides attractive and customizable tooltips for any element.
15499  * @singleton
15500  */
15501 Roo.QuickTips = function(){
15502     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15503     var ce, bd, xy, dd;
15504     var visible = false, disabled = true, inited = false;
15505     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15506     
15507     var onOver = function(e){
15508         if(disabled){
15509             return;
15510         }
15511         var t = e.getTarget();
15512         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15513             return;
15514         }
15515         if(ce && t == ce.el){
15516             clearTimeout(hideProc);
15517             return;
15518         }
15519         if(t && tagEls[t.id]){
15520             tagEls[t.id].el = t;
15521             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15522             return;
15523         }
15524         var ttp, et = Roo.fly(t);
15525         var ns = cfg.namespace;
15526         if(tm.interceptTitles && t.title){
15527             ttp = t.title;
15528             t.qtip = ttp;
15529             t.removeAttribute("title");
15530             e.preventDefault();
15531         }else{
15532             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15533         }
15534         if(ttp){
15535             showProc = show.defer(tm.showDelay, tm, [{
15536                 el: t, 
15537                 text: ttp, 
15538                 width: et.getAttributeNS(ns, cfg.width),
15539                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15540                 title: et.getAttributeNS(ns, cfg.title),
15541                     cls: et.getAttributeNS(ns, cfg.cls)
15542             }]);
15543         }
15544     };
15545     
15546     var onOut = function(e){
15547         clearTimeout(showProc);
15548         var t = e.getTarget();
15549         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15550             hideProc = setTimeout(hide, tm.hideDelay);
15551         }
15552     };
15553     
15554     var onMove = function(e){
15555         if(disabled){
15556             return;
15557         }
15558         xy = e.getXY();
15559         xy[1] += 18;
15560         if(tm.trackMouse && ce){
15561             el.setXY(xy);
15562         }
15563     };
15564     
15565     var onDown = function(e){
15566         clearTimeout(showProc);
15567         clearTimeout(hideProc);
15568         if(!e.within(el)){
15569             if(tm.hideOnClick){
15570                 hide();
15571                 tm.disable();
15572                 tm.enable.defer(100, tm);
15573             }
15574         }
15575     };
15576     
15577     var getPad = function(){
15578         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15579     };
15580
15581     var show = function(o){
15582         if(disabled){
15583             return;
15584         }
15585         clearTimeout(dismissProc);
15586         ce = o;
15587         if(removeCls){ // in case manually hidden
15588             el.removeClass(removeCls);
15589             removeCls = null;
15590         }
15591         if(ce.cls){
15592             el.addClass(ce.cls);
15593             removeCls = ce.cls;
15594         }
15595         if(ce.title){
15596             tipTitle.update(ce.title);
15597             tipTitle.show();
15598         }else{
15599             tipTitle.update('');
15600             tipTitle.hide();
15601         }
15602         el.dom.style.width  = tm.maxWidth+'px';
15603         //tipBody.dom.style.width = '';
15604         tipBodyText.update(o.text);
15605         var p = getPad(), w = ce.width;
15606         if(!w){
15607             var td = tipBodyText.dom;
15608             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15609             if(aw > tm.maxWidth){
15610                 w = tm.maxWidth;
15611             }else if(aw < tm.minWidth){
15612                 w = tm.minWidth;
15613             }else{
15614                 w = aw;
15615             }
15616         }
15617         //tipBody.setWidth(w);
15618         el.setWidth(parseInt(w, 10) + p);
15619         if(ce.autoHide === false){
15620             close.setDisplayed(true);
15621             if(dd){
15622                 dd.unlock();
15623             }
15624         }else{
15625             close.setDisplayed(false);
15626             if(dd){
15627                 dd.lock();
15628             }
15629         }
15630         if(xy){
15631             el.avoidY = xy[1]-18;
15632             el.setXY(xy);
15633         }
15634         if(tm.animate){
15635             el.setOpacity(.1);
15636             el.setStyle("visibility", "visible");
15637             el.fadeIn({callback: afterShow});
15638         }else{
15639             afterShow();
15640         }
15641     };
15642     
15643     var afterShow = function(){
15644         if(ce){
15645             el.show();
15646             esc.enable();
15647             if(tm.autoDismiss && ce.autoHide !== false){
15648                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15649             }
15650         }
15651     };
15652     
15653     var hide = function(noanim){
15654         clearTimeout(dismissProc);
15655         clearTimeout(hideProc);
15656         ce = null;
15657         if(el.isVisible()){
15658             esc.disable();
15659             if(noanim !== true && tm.animate){
15660                 el.fadeOut({callback: afterHide});
15661             }else{
15662                 afterHide();
15663             } 
15664         }
15665     };
15666     
15667     var afterHide = function(){
15668         el.hide();
15669         if(removeCls){
15670             el.removeClass(removeCls);
15671             removeCls = null;
15672         }
15673     };
15674     
15675     return {
15676         /**
15677         * @cfg {Number} minWidth
15678         * The minimum width of the quick tip (defaults to 40)
15679         */
15680        minWidth : 40,
15681         /**
15682         * @cfg {Number} maxWidth
15683         * The maximum width of the quick tip (defaults to 300)
15684         */
15685        maxWidth : 300,
15686         /**
15687         * @cfg {Boolean} interceptTitles
15688         * True to automatically use the element's DOM title value if available (defaults to false)
15689         */
15690        interceptTitles : false,
15691         /**
15692         * @cfg {Boolean} trackMouse
15693         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15694         */
15695        trackMouse : false,
15696         /**
15697         * @cfg {Boolean} hideOnClick
15698         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15699         */
15700        hideOnClick : true,
15701         /**
15702         * @cfg {Number} showDelay
15703         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15704         */
15705        showDelay : 500,
15706         /**
15707         * @cfg {Number} hideDelay
15708         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15709         */
15710        hideDelay : 200,
15711         /**
15712         * @cfg {Boolean} autoHide
15713         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15714         * Used in conjunction with hideDelay.
15715         */
15716        autoHide : true,
15717         /**
15718         * @cfg {Boolean}
15719         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15720         * (defaults to true).  Used in conjunction with autoDismissDelay.
15721         */
15722        autoDismiss : true,
15723         /**
15724         * @cfg {Number}
15725         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15726         */
15727        autoDismissDelay : 5000,
15728        /**
15729         * @cfg {Boolean} animate
15730         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15731         */
15732        animate : false,
15733
15734        /**
15735         * @cfg {String} title
15736         * Title text to display (defaults to '').  This can be any valid HTML markup.
15737         */
15738         title: '',
15739        /**
15740         * @cfg {String} text
15741         * Body text to display (defaults to '').  This can be any valid HTML markup.
15742         */
15743         text : '',
15744        /**
15745         * @cfg {String} cls
15746         * A CSS class to apply to the base quick tip element (defaults to '').
15747         */
15748         cls : '',
15749        /**
15750         * @cfg {Number} width
15751         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15752         * minWidth or maxWidth.
15753         */
15754         width : null,
15755
15756     /**
15757      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15758      * or display QuickTips in a page.
15759      */
15760        init : function(){
15761           tm = Roo.QuickTips;
15762           cfg = tm.tagConfig;
15763           if(!inited){
15764               if(!Roo.isReady){ // allow calling of init() before onReady
15765                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15766                   return;
15767               }
15768               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15769               el.fxDefaults = {stopFx: true};
15770               // maximum custom styling
15771               //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>');
15772               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>');              
15773               tipTitle = el.child('h3');
15774               tipTitle.enableDisplayMode("block");
15775               tipBody = el.child('div.x-tip-bd');
15776               tipBodyText = el.child('div.x-tip-bd-inner');
15777               //bdLeft = el.child('div.x-tip-bd-left');
15778               //bdRight = el.child('div.x-tip-bd-right');
15779               close = el.child('div.x-tip-close');
15780               close.enableDisplayMode("block");
15781               close.on("click", hide);
15782               var d = Roo.get(document);
15783               d.on("mousedown", onDown);
15784               d.on("mouseover", onOver);
15785               d.on("mouseout", onOut);
15786               d.on("mousemove", onMove);
15787               esc = d.addKeyListener(27, hide);
15788               esc.disable();
15789               if(Roo.dd.DD){
15790                   dd = el.initDD("default", null, {
15791                       onDrag : function(){
15792                           el.sync();  
15793                       }
15794                   });
15795                   dd.setHandleElId(tipTitle.id);
15796                   dd.lock();
15797               }
15798               inited = true;
15799           }
15800           this.enable(); 
15801        },
15802
15803     /**
15804      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15805      * are supported:
15806      * <pre>
15807 Property    Type                   Description
15808 ----------  ---------------------  ------------------------------------------------------------------------
15809 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15810      * </ul>
15811      * @param {Object} config The config object
15812      */
15813        register : function(config){
15814            var cs = config instanceof Array ? config : arguments;
15815            for(var i = 0, len = cs.length; i < len; i++) {
15816                var c = cs[i];
15817                var target = c.target;
15818                if(target){
15819                    if(target instanceof Array){
15820                        for(var j = 0, jlen = target.length; j < jlen; j++){
15821                            tagEls[target[j]] = c;
15822                        }
15823                    }else{
15824                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15825                    }
15826                }
15827            }
15828        },
15829
15830     /**
15831      * Removes this quick tip from its element and destroys it.
15832      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15833      */
15834        unregister : function(el){
15835            delete tagEls[Roo.id(el)];
15836        },
15837
15838     /**
15839      * Enable this quick tip.
15840      */
15841        enable : function(){
15842            if(inited && disabled){
15843                locks.pop();
15844                if(locks.length < 1){
15845                    disabled = false;
15846                }
15847            }
15848        },
15849
15850     /**
15851      * Disable this quick tip.
15852      */
15853        disable : function(){
15854           disabled = true;
15855           clearTimeout(showProc);
15856           clearTimeout(hideProc);
15857           clearTimeout(dismissProc);
15858           if(ce){
15859               hide(true);
15860           }
15861           locks.push(1);
15862        },
15863
15864     /**
15865      * Returns true if the quick tip is enabled, else false.
15866      */
15867        isEnabled : function(){
15868             return !disabled;
15869        },
15870
15871         // private
15872        tagConfig : {
15873            namespace : "ext",
15874            attribute : "qtip",
15875            width : "width",
15876            target : "target",
15877            title : "qtitle",
15878            hide : "hide",
15879            cls : "qclass"
15880        }
15881    };
15882 }();
15883
15884 // backwards compat
15885 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15886  * Based on:
15887  * Ext JS Library 1.1.1
15888  * Copyright(c) 2006-2007, Ext JS, LLC.
15889  *
15890  * Originally Released Under LGPL - original licence link has changed is not relivant.
15891  *
15892  * Fork - LGPL
15893  * <script type="text/javascript">
15894  */
15895  
15896
15897 /**
15898  * @class Roo.tree.TreePanel
15899  * @extends Roo.data.Tree
15900
15901  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15902  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15903  * @cfg {Boolean} enableDD true to enable drag and drop
15904  * @cfg {Boolean} enableDrag true to enable just drag
15905  * @cfg {Boolean} enableDrop true to enable just drop
15906  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15907  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15908  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15909  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15910  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15911  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15912  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15913  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15914  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15915  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15916  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15917  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15918  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15919  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15920  * @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>
15921  * @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>
15922  * 
15923  * @constructor
15924  * @param {String/HTMLElement/Element} el The container element
15925  * @param {Object} config
15926  */
15927 Roo.tree.TreePanel = function(el, config){
15928     var root = false;
15929     var loader = false;
15930     if (config.root) {
15931         root = config.root;
15932         delete config.root;
15933     }
15934     if (config.loader) {
15935         loader = config.loader;
15936         delete config.loader;
15937     }
15938     
15939     Roo.apply(this, config);
15940     Roo.tree.TreePanel.superclass.constructor.call(this);
15941     this.el = Roo.get(el);
15942     this.el.addClass('x-tree');
15943     //console.log(root);
15944     if (root) {
15945         this.setRootNode( Roo.factory(root, Roo.tree));
15946     }
15947     if (loader) {
15948         this.loader = Roo.factory(loader, Roo.tree);
15949     }
15950    /**
15951     * Read-only. The id of the container element becomes this TreePanel's id.
15952     */
15953     this.id = this.el.id;
15954     this.addEvents({
15955         /**
15956         * @event beforeload
15957         * Fires before a node is loaded, return false to cancel
15958         * @param {Node} node The node being loaded
15959         */
15960         "beforeload" : true,
15961         /**
15962         * @event load
15963         * Fires when a node is loaded
15964         * @param {Node} node The node that was loaded
15965         */
15966         "load" : true,
15967         /**
15968         * @event textchange
15969         * Fires when the text for a node is changed
15970         * @param {Node} node The node
15971         * @param {String} text The new text
15972         * @param {String} oldText The old text
15973         */
15974         "textchange" : true,
15975         /**
15976         * @event beforeexpand
15977         * Fires before a node is expanded, return false to cancel.
15978         * @param {Node} node The node
15979         * @param {Boolean} deep
15980         * @param {Boolean} anim
15981         */
15982         "beforeexpand" : true,
15983         /**
15984         * @event beforecollapse
15985         * Fires before a node is collapsed, return false to cancel.
15986         * @param {Node} node The node
15987         * @param {Boolean} deep
15988         * @param {Boolean} anim
15989         */
15990         "beforecollapse" : true,
15991         /**
15992         * @event expand
15993         * Fires when a node is expanded
15994         * @param {Node} node The node
15995         */
15996         "expand" : true,
15997         /**
15998         * @event disabledchange
15999         * Fires when the disabled status of a node changes
16000         * @param {Node} node The node
16001         * @param {Boolean} disabled
16002         */
16003         "disabledchange" : true,
16004         /**
16005         * @event collapse
16006         * Fires when a node is collapsed
16007         * @param {Node} node The node
16008         */
16009         "collapse" : true,
16010         /**
16011         * @event beforeclick
16012         * Fires before click processing on a node. Return false to cancel the default action.
16013         * @param {Node} node The node
16014         * @param {Roo.EventObject} e The event object
16015         */
16016         "beforeclick":true,
16017         /**
16018         * @event checkchange
16019         * Fires when a node with a checkbox's checked property changes
16020         * @param {Node} this This node
16021         * @param {Boolean} checked
16022         */
16023         "checkchange":true,
16024         /**
16025         * @event click
16026         * Fires when a node is clicked
16027         * @param {Node} node The node
16028         * @param {Roo.EventObject} e The event object
16029         */
16030         "click":true,
16031         /**
16032         * @event dblclick
16033         * Fires when a node is double clicked
16034         * @param {Node} node The node
16035         * @param {Roo.EventObject} e The event object
16036         */
16037         "dblclick":true,
16038         /**
16039         * @event contextmenu
16040         * Fires when a node is right clicked
16041         * @param {Node} node The node
16042         * @param {Roo.EventObject} e The event object
16043         */
16044         "contextmenu":true,
16045         /**
16046         * @event beforechildrenrendered
16047         * Fires right before the child nodes for a node are rendered
16048         * @param {Node} node The node
16049         */
16050         "beforechildrenrendered":true,
16051         /**
16052         * @event startdrag
16053         * Fires when a node starts being dragged
16054         * @param {Roo.tree.TreePanel} this
16055         * @param {Roo.tree.TreeNode} node
16056         * @param {event} e The raw browser event
16057         */ 
16058        "startdrag" : true,
16059        /**
16060         * @event enddrag
16061         * Fires when a drag operation is complete
16062         * @param {Roo.tree.TreePanel} this
16063         * @param {Roo.tree.TreeNode} node
16064         * @param {event} e The raw browser event
16065         */
16066        "enddrag" : true,
16067        /**
16068         * @event dragdrop
16069         * Fires when a dragged node is dropped on a valid DD target
16070         * @param {Roo.tree.TreePanel} this
16071         * @param {Roo.tree.TreeNode} node
16072         * @param {DD} dd The dd it was dropped on
16073         * @param {event} e The raw browser event
16074         */
16075        "dragdrop" : true,
16076        /**
16077         * @event beforenodedrop
16078         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16079         * passed to handlers has the following properties:<br />
16080         * <ul style="padding:5px;padding-left:16px;">
16081         * <li>tree - The TreePanel</li>
16082         * <li>target - The node being targeted for the drop</li>
16083         * <li>data - The drag data from the drag source</li>
16084         * <li>point - The point of the drop - append, above or below</li>
16085         * <li>source - The drag source</li>
16086         * <li>rawEvent - Raw mouse event</li>
16087         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16088         * to be inserted by setting them on this object.</li>
16089         * <li>cancel - Set this to true to cancel the drop.</li>
16090         * </ul>
16091         * @param {Object} dropEvent
16092         */
16093        "beforenodedrop" : true,
16094        /**
16095         * @event nodedrop
16096         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16097         * passed to handlers has the following properties:<br />
16098         * <ul style="padding:5px;padding-left:16px;">
16099         * <li>tree - The TreePanel</li>
16100         * <li>target - The node being targeted for the drop</li>
16101         * <li>data - The drag data from the drag source</li>
16102         * <li>point - The point of the drop - append, above or below</li>
16103         * <li>source - The drag source</li>
16104         * <li>rawEvent - Raw mouse event</li>
16105         * <li>dropNode - Dropped node(s).</li>
16106         * </ul>
16107         * @param {Object} dropEvent
16108         */
16109        "nodedrop" : true,
16110         /**
16111         * @event nodedragover
16112         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16113         * passed to handlers has the following properties:<br />
16114         * <ul style="padding:5px;padding-left:16px;">
16115         * <li>tree - The TreePanel</li>
16116         * <li>target - The node being targeted for the drop</li>
16117         * <li>data - The drag data from the drag source</li>
16118         * <li>point - The point of the drop - append, above or below</li>
16119         * <li>source - The drag source</li>
16120         * <li>rawEvent - Raw mouse event</li>
16121         * <li>dropNode - Drop node(s) provided by the source.</li>
16122         * <li>cancel - Set this to true to signal drop not allowed.</li>
16123         * </ul>
16124         * @param {Object} dragOverEvent
16125         */
16126        "nodedragover" : true
16127         
16128     });
16129     if(this.singleExpand){
16130        this.on("beforeexpand", this.restrictExpand, this);
16131     }
16132     if (this.editor) {
16133         this.editor.tree = this;
16134         this.editor = Roo.factory(this.editor, Roo.tree);
16135     }
16136     
16137     if (this.selModel) {
16138         this.selModel = Roo.factory(this.selModel, Roo.tree);
16139     }
16140    
16141 };
16142 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16143     rootVisible : true,
16144     animate: Roo.enableFx,
16145     lines : true,
16146     enableDD : false,
16147     hlDrop : Roo.enableFx,
16148   
16149     renderer: false,
16150     
16151     rendererTip: false,
16152     // private
16153     restrictExpand : function(node){
16154         var p = node.parentNode;
16155         if(p){
16156             if(p.expandedChild && p.expandedChild.parentNode == p){
16157                 p.expandedChild.collapse();
16158             }
16159             p.expandedChild = node;
16160         }
16161     },
16162
16163     // private override
16164     setRootNode : function(node){
16165         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16166         if(!this.rootVisible){
16167             node.ui = new Roo.tree.RootTreeNodeUI(node);
16168         }
16169         return node;
16170     },
16171
16172     /**
16173      * Returns the container element for this TreePanel
16174      */
16175     getEl : function(){
16176         return this.el;
16177     },
16178
16179     /**
16180      * Returns the default TreeLoader for this TreePanel
16181      */
16182     getLoader : function(){
16183         return this.loader;
16184     },
16185
16186     /**
16187      * Expand all nodes
16188      */
16189     expandAll : function(){
16190         this.root.expand(true);
16191     },
16192
16193     /**
16194      * Collapse all nodes
16195      */
16196     collapseAll : function(){
16197         this.root.collapse(true);
16198     },
16199
16200     /**
16201      * Returns the selection model used by this TreePanel
16202      */
16203     getSelectionModel : function(){
16204         if(!this.selModel){
16205             this.selModel = new Roo.tree.DefaultSelectionModel();
16206         }
16207         return this.selModel;
16208     },
16209
16210     /**
16211      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16212      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16213      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16214      * @return {Array}
16215      */
16216     getChecked : function(a, startNode){
16217         startNode = startNode || this.root;
16218         var r = [];
16219         var f = function(){
16220             if(this.attributes.checked){
16221                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16222             }
16223         }
16224         startNode.cascade(f);
16225         return r;
16226     },
16227
16228     /**
16229      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16230      * @param {String} path
16231      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16232      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16233      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16234      */
16235     expandPath : function(path, attr, callback){
16236         attr = attr || "id";
16237         var keys = path.split(this.pathSeparator);
16238         var curNode = this.root;
16239         if(curNode.attributes[attr] != keys[1]){ // invalid root
16240             if(callback){
16241                 callback(false, null);
16242             }
16243             return;
16244         }
16245         var index = 1;
16246         var f = function(){
16247             if(++index == keys.length){
16248                 if(callback){
16249                     callback(true, curNode);
16250                 }
16251                 return;
16252             }
16253             var c = curNode.findChild(attr, keys[index]);
16254             if(!c){
16255                 if(callback){
16256                     callback(false, curNode);
16257                 }
16258                 return;
16259             }
16260             curNode = c;
16261             c.expand(false, false, f);
16262         };
16263         curNode.expand(false, false, f);
16264     },
16265
16266     /**
16267      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16268      * @param {String} path
16269      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16270      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16271      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16272      */
16273     selectPath : function(path, attr, callback){
16274         attr = attr || "id";
16275         var keys = path.split(this.pathSeparator);
16276         var v = keys.pop();
16277         if(keys.length > 0){
16278             var f = function(success, node){
16279                 if(success && node){
16280                     var n = node.findChild(attr, v);
16281                     if(n){
16282                         n.select();
16283                         if(callback){
16284                             callback(true, n);
16285                         }
16286                     }else if(callback){
16287                         callback(false, n);
16288                     }
16289                 }else{
16290                     if(callback){
16291                         callback(false, n);
16292                     }
16293                 }
16294             };
16295             this.expandPath(keys.join(this.pathSeparator), attr, f);
16296         }else{
16297             this.root.select();
16298             if(callback){
16299                 callback(true, this.root);
16300             }
16301         }
16302     },
16303
16304     getTreeEl : function(){
16305         return this.el;
16306     },
16307
16308     /**
16309      * Trigger rendering of this TreePanel
16310      */
16311     render : function(){
16312         if (this.innerCt) {
16313             return this; // stop it rendering more than once!!
16314         }
16315         
16316         this.innerCt = this.el.createChild({tag:"ul",
16317                cls:"x-tree-root-ct " +
16318                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16319
16320         if(this.containerScroll){
16321             Roo.dd.ScrollManager.register(this.el);
16322         }
16323         if((this.enableDD || this.enableDrop) && !this.dropZone){
16324            /**
16325             * The dropZone used by this tree if drop is enabled
16326             * @type Roo.tree.TreeDropZone
16327             */
16328              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16329                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16330            });
16331         }
16332         if((this.enableDD || this.enableDrag) && !this.dragZone){
16333            /**
16334             * The dragZone used by this tree if drag is enabled
16335             * @type Roo.tree.TreeDragZone
16336             */
16337             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16338                ddGroup: this.ddGroup || "TreeDD",
16339                scroll: this.ddScroll
16340            });
16341         }
16342         this.getSelectionModel().init(this);
16343         if (!this.root) {
16344             Roo.log("ROOT not set in tree");
16345             return this;
16346         }
16347         this.root.render();
16348         if(!this.rootVisible){
16349             this.root.renderChildren();
16350         }
16351         return this;
16352     }
16353 });/*
16354  * Based on:
16355  * Ext JS Library 1.1.1
16356  * Copyright(c) 2006-2007, Ext JS, LLC.
16357  *
16358  * Originally Released Under LGPL - original licence link has changed is not relivant.
16359  *
16360  * Fork - LGPL
16361  * <script type="text/javascript">
16362  */
16363  
16364
16365 /**
16366  * @class Roo.tree.DefaultSelectionModel
16367  * @extends Roo.util.Observable
16368  * The default single selection for a TreePanel.
16369  * @param {Object} cfg Configuration
16370  */
16371 Roo.tree.DefaultSelectionModel = function(cfg){
16372    this.selNode = null;
16373    
16374    
16375    
16376    this.addEvents({
16377        /**
16378         * @event selectionchange
16379         * Fires when the selected node changes
16380         * @param {DefaultSelectionModel} this
16381         * @param {TreeNode} node the new selection
16382         */
16383        "selectionchange" : true,
16384
16385        /**
16386         * @event beforeselect
16387         * Fires before the selected node changes, return false to cancel the change
16388         * @param {DefaultSelectionModel} this
16389         * @param {TreeNode} node the new selection
16390         * @param {TreeNode} node the old selection
16391         */
16392        "beforeselect" : true
16393    });
16394    
16395     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16396 };
16397
16398 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16399     init : function(tree){
16400         this.tree = tree;
16401         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16402         tree.on("click", this.onNodeClick, this);
16403     },
16404     
16405     onNodeClick : function(node, e){
16406         if (e.ctrlKey && this.selNode == node)  {
16407             this.unselect(node);
16408             return;
16409         }
16410         this.select(node);
16411     },
16412     
16413     /**
16414      * Select a node.
16415      * @param {TreeNode} node The node to select
16416      * @return {TreeNode} The selected node
16417      */
16418     select : function(node){
16419         var last = this.selNode;
16420         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16421             if(last){
16422                 last.ui.onSelectedChange(false);
16423             }
16424             this.selNode = node;
16425             node.ui.onSelectedChange(true);
16426             this.fireEvent("selectionchange", this, node, last);
16427         }
16428         return node;
16429     },
16430     
16431     /**
16432      * Deselect a node.
16433      * @param {TreeNode} node The node to unselect
16434      */
16435     unselect : function(node){
16436         if(this.selNode == node){
16437             this.clearSelections();
16438         }    
16439     },
16440     
16441     /**
16442      * Clear all selections
16443      */
16444     clearSelections : function(){
16445         var n = this.selNode;
16446         if(n){
16447             n.ui.onSelectedChange(false);
16448             this.selNode = null;
16449             this.fireEvent("selectionchange", this, null);
16450         }
16451         return n;
16452     },
16453     
16454     /**
16455      * Get the selected node
16456      * @return {TreeNode} The selected node
16457      */
16458     getSelectedNode : function(){
16459         return this.selNode;    
16460     },
16461     
16462     /**
16463      * Returns true if the node is selected
16464      * @param {TreeNode} node The node to check
16465      * @return {Boolean}
16466      */
16467     isSelected : function(node){
16468         return this.selNode == node;  
16469     },
16470
16471     /**
16472      * Selects the node above the selected node in the tree, intelligently walking the nodes
16473      * @return TreeNode The new selection
16474      */
16475     selectPrevious : function(){
16476         var s = this.selNode || this.lastSelNode;
16477         if(!s){
16478             return null;
16479         }
16480         var ps = s.previousSibling;
16481         if(ps){
16482             if(!ps.isExpanded() || ps.childNodes.length < 1){
16483                 return this.select(ps);
16484             } else{
16485                 var lc = ps.lastChild;
16486                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16487                     lc = lc.lastChild;
16488                 }
16489                 return this.select(lc);
16490             }
16491         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16492             return this.select(s.parentNode);
16493         }
16494         return null;
16495     },
16496
16497     /**
16498      * Selects the node above the selected node in the tree, intelligently walking the nodes
16499      * @return TreeNode The new selection
16500      */
16501     selectNext : function(){
16502         var s = this.selNode || this.lastSelNode;
16503         if(!s){
16504             return null;
16505         }
16506         if(s.firstChild && s.isExpanded()){
16507              return this.select(s.firstChild);
16508          }else if(s.nextSibling){
16509              return this.select(s.nextSibling);
16510          }else if(s.parentNode){
16511             var newS = null;
16512             s.parentNode.bubble(function(){
16513                 if(this.nextSibling){
16514                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16515                     return false;
16516                 }
16517             });
16518             return newS;
16519          }
16520         return null;
16521     },
16522
16523     onKeyDown : function(e){
16524         var s = this.selNode || this.lastSelNode;
16525         // undesirable, but required
16526         var sm = this;
16527         if(!s){
16528             return;
16529         }
16530         var k = e.getKey();
16531         switch(k){
16532              case e.DOWN:
16533                  e.stopEvent();
16534                  this.selectNext();
16535              break;
16536              case e.UP:
16537                  e.stopEvent();
16538                  this.selectPrevious();
16539              break;
16540              case e.RIGHT:
16541                  e.preventDefault();
16542                  if(s.hasChildNodes()){
16543                      if(!s.isExpanded()){
16544                          s.expand();
16545                      }else if(s.firstChild){
16546                          this.select(s.firstChild, e);
16547                      }
16548                  }
16549              break;
16550              case e.LEFT:
16551                  e.preventDefault();
16552                  if(s.hasChildNodes() && s.isExpanded()){
16553                      s.collapse();
16554                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16555                      this.select(s.parentNode, e);
16556                  }
16557              break;
16558         };
16559     }
16560 });
16561
16562 /**
16563  * @class Roo.tree.MultiSelectionModel
16564  * @extends Roo.util.Observable
16565  * Multi selection for a TreePanel.
16566  * @param {Object} cfg Configuration
16567  */
16568 Roo.tree.MultiSelectionModel = function(){
16569    this.selNodes = [];
16570    this.selMap = {};
16571    this.addEvents({
16572        /**
16573         * @event selectionchange
16574         * Fires when the selected nodes change
16575         * @param {MultiSelectionModel} this
16576         * @param {Array} nodes Array of the selected nodes
16577         */
16578        "selectionchange" : true
16579    });
16580    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16581    
16582 };
16583
16584 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16585     init : function(tree){
16586         this.tree = tree;
16587         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16588         tree.on("click", this.onNodeClick, this);
16589     },
16590     
16591     onNodeClick : function(node, e){
16592         this.select(node, e, e.ctrlKey);
16593     },
16594     
16595     /**
16596      * Select a node.
16597      * @param {TreeNode} node The node to select
16598      * @param {EventObject} e (optional) An event associated with the selection
16599      * @param {Boolean} keepExisting True to retain existing selections
16600      * @return {TreeNode} The selected node
16601      */
16602     select : function(node, e, keepExisting){
16603         if(keepExisting !== true){
16604             this.clearSelections(true);
16605         }
16606         if(this.isSelected(node)){
16607             this.lastSelNode = node;
16608             return node;
16609         }
16610         this.selNodes.push(node);
16611         this.selMap[node.id] = node;
16612         this.lastSelNode = node;
16613         node.ui.onSelectedChange(true);
16614         this.fireEvent("selectionchange", this, this.selNodes);
16615         return node;
16616     },
16617     
16618     /**
16619      * Deselect a node.
16620      * @param {TreeNode} node The node to unselect
16621      */
16622     unselect : function(node){
16623         if(this.selMap[node.id]){
16624             node.ui.onSelectedChange(false);
16625             var sn = this.selNodes;
16626             var index = -1;
16627             if(sn.indexOf){
16628                 index = sn.indexOf(node);
16629             }else{
16630                 for(var i = 0, len = sn.length; i < len; i++){
16631                     if(sn[i] == node){
16632                         index = i;
16633                         break;
16634                     }
16635                 }
16636             }
16637             if(index != -1){
16638                 this.selNodes.splice(index, 1);
16639             }
16640             delete this.selMap[node.id];
16641             this.fireEvent("selectionchange", this, this.selNodes);
16642         }
16643     },
16644     
16645     /**
16646      * Clear all selections
16647      */
16648     clearSelections : function(suppressEvent){
16649         var sn = this.selNodes;
16650         if(sn.length > 0){
16651             for(var i = 0, len = sn.length; i < len; i++){
16652                 sn[i].ui.onSelectedChange(false);
16653             }
16654             this.selNodes = [];
16655             this.selMap = {};
16656             if(suppressEvent !== true){
16657                 this.fireEvent("selectionchange", this, this.selNodes);
16658             }
16659         }
16660     },
16661     
16662     /**
16663      * Returns true if the node is selected
16664      * @param {TreeNode} node The node to check
16665      * @return {Boolean}
16666      */
16667     isSelected : function(node){
16668         return this.selMap[node.id] ? true : false;  
16669     },
16670     
16671     /**
16672      * Returns an array of the selected nodes
16673      * @return {Array}
16674      */
16675     getSelectedNodes : function(){
16676         return this.selNodes;    
16677     },
16678
16679     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16680
16681     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16682
16683     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16684 });/*
16685  * Based on:
16686  * Ext JS Library 1.1.1
16687  * Copyright(c) 2006-2007, Ext JS, LLC.
16688  *
16689  * Originally Released Under LGPL - original licence link has changed is not relivant.
16690  *
16691  * Fork - LGPL
16692  * <script type="text/javascript">
16693  */
16694  
16695 /**
16696  * @class Roo.tree.TreeNode
16697  * @extends Roo.data.Node
16698  * @cfg {String} text The text for this node
16699  * @cfg {Boolean} expanded true to start the node expanded
16700  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16701  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16702  * @cfg {Boolean} disabled true to start the node disabled
16703  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16704  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16705  * @cfg {String} cls A css class to be added to the node
16706  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16707  * @cfg {String} href URL of the link used for the node (defaults to #)
16708  * @cfg {String} hrefTarget target frame for the link
16709  * @cfg {String} qtip An Ext QuickTip for the node
16710  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16711  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16712  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16713  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16714  * (defaults to undefined with no checkbox rendered)
16715  * @constructor
16716  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16717  */
16718 Roo.tree.TreeNode = function(attributes){
16719     attributes = attributes || {};
16720     if(typeof attributes == "string"){
16721         attributes = {text: attributes};
16722     }
16723     this.childrenRendered = false;
16724     this.rendered = false;
16725     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16726     this.expanded = attributes.expanded === true;
16727     this.isTarget = attributes.isTarget !== false;
16728     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16729     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16730
16731     /**
16732      * Read-only. The text for this node. To change it use setText().
16733      * @type String
16734      */
16735     this.text = attributes.text;
16736     /**
16737      * True if this node is disabled.
16738      * @type Boolean
16739      */
16740     this.disabled = attributes.disabled === true;
16741
16742     this.addEvents({
16743         /**
16744         * @event textchange
16745         * Fires when the text for this node is changed
16746         * @param {Node} this This node
16747         * @param {String} text The new text
16748         * @param {String} oldText The old text
16749         */
16750         "textchange" : true,
16751         /**
16752         * @event beforeexpand
16753         * Fires before this node is expanded, return false to cancel.
16754         * @param {Node} this This node
16755         * @param {Boolean} deep
16756         * @param {Boolean} anim
16757         */
16758         "beforeexpand" : true,
16759         /**
16760         * @event beforecollapse
16761         * Fires before this node is collapsed, return false to cancel.
16762         * @param {Node} this This node
16763         * @param {Boolean} deep
16764         * @param {Boolean} anim
16765         */
16766         "beforecollapse" : true,
16767         /**
16768         * @event expand
16769         * Fires when this node is expanded
16770         * @param {Node} this This node
16771         */
16772         "expand" : true,
16773         /**
16774         * @event disabledchange
16775         * Fires when the disabled status of this node changes
16776         * @param {Node} this This node
16777         * @param {Boolean} disabled
16778         */
16779         "disabledchange" : true,
16780         /**
16781         * @event collapse
16782         * Fires when this node is collapsed
16783         * @param {Node} this This node
16784         */
16785         "collapse" : true,
16786         /**
16787         * @event beforeclick
16788         * Fires before click processing. Return false to cancel the default action.
16789         * @param {Node} this This node
16790         * @param {Roo.EventObject} e The event object
16791         */
16792         "beforeclick":true,
16793         /**
16794         * @event checkchange
16795         * Fires when a node with a checkbox's checked property changes
16796         * @param {Node} this This node
16797         * @param {Boolean} checked
16798         */
16799         "checkchange":true,
16800         /**
16801         * @event click
16802         * Fires when this node is clicked
16803         * @param {Node} this This node
16804         * @param {Roo.EventObject} e The event object
16805         */
16806         "click":true,
16807         /**
16808         * @event dblclick
16809         * Fires when this node is double clicked
16810         * @param {Node} this This node
16811         * @param {Roo.EventObject} e The event object
16812         */
16813         "dblclick":true,
16814         /**
16815         * @event contextmenu
16816         * Fires when this node is right clicked
16817         * @param {Node} this This node
16818         * @param {Roo.EventObject} e The event object
16819         */
16820         "contextmenu":true,
16821         /**
16822         * @event beforechildrenrendered
16823         * Fires right before the child nodes for this node are rendered
16824         * @param {Node} this This node
16825         */
16826         "beforechildrenrendered":true
16827     });
16828
16829     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16830
16831     /**
16832      * Read-only. The UI for this node
16833      * @type TreeNodeUI
16834      */
16835     this.ui = new uiClass(this);
16836     
16837     // finally support items[]
16838     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16839         return;
16840     }
16841     
16842     
16843     Roo.each(this.attributes.items, function(c) {
16844         this.appendChild(Roo.factory(c,Roo.Tree));
16845     }, this);
16846     delete this.attributes.items;
16847     
16848     
16849     
16850 };
16851 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16852     preventHScroll: true,
16853     /**
16854      * Returns true if this node is expanded
16855      * @return {Boolean}
16856      */
16857     isExpanded : function(){
16858         return this.expanded;
16859     },
16860
16861     /**
16862      * Returns the UI object for this node
16863      * @return {TreeNodeUI}
16864      */
16865     getUI : function(){
16866         return this.ui;
16867     },
16868
16869     // private override
16870     setFirstChild : function(node){
16871         var of = this.firstChild;
16872         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16873         if(this.childrenRendered && of && node != of){
16874             of.renderIndent(true, true);
16875         }
16876         if(this.rendered){
16877             this.renderIndent(true, true);
16878         }
16879     },
16880
16881     // private override
16882     setLastChild : function(node){
16883         var ol = this.lastChild;
16884         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16885         if(this.childrenRendered && ol && node != ol){
16886             ol.renderIndent(true, true);
16887         }
16888         if(this.rendered){
16889             this.renderIndent(true, true);
16890         }
16891     },
16892
16893     // these methods are overridden to provide lazy rendering support
16894     // private override
16895     appendChild : function()
16896     {
16897         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16898         if(node && this.childrenRendered){
16899             node.render();
16900         }
16901         this.ui.updateExpandIcon();
16902         return node;
16903     },
16904
16905     // private override
16906     removeChild : function(node){
16907         this.ownerTree.getSelectionModel().unselect(node);
16908         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16909         // if it's been rendered remove dom node
16910         if(this.childrenRendered){
16911             node.ui.remove();
16912         }
16913         if(this.childNodes.length < 1){
16914             this.collapse(false, false);
16915         }else{
16916             this.ui.updateExpandIcon();
16917         }
16918         if(!this.firstChild) {
16919             this.childrenRendered = false;
16920         }
16921         return node;
16922     },
16923
16924     // private override
16925     insertBefore : function(node, refNode){
16926         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16927         if(newNode && refNode && this.childrenRendered){
16928             node.render();
16929         }
16930         this.ui.updateExpandIcon();
16931         return newNode;
16932     },
16933
16934     /**
16935      * Sets the text for this node
16936      * @param {String} text
16937      */
16938     setText : function(text){
16939         var oldText = this.text;
16940         this.text = text;
16941         this.attributes.text = text;
16942         if(this.rendered){ // event without subscribing
16943             this.ui.onTextChange(this, text, oldText);
16944         }
16945         this.fireEvent("textchange", this, text, oldText);
16946     },
16947
16948     /**
16949      * Triggers selection of this node
16950      */
16951     select : function(){
16952         this.getOwnerTree().getSelectionModel().select(this);
16953     },
16954
16955     /**
16956      * Triggers deselection of this node
16957      */
16958     unselect : function(){
16959         this.getOwnerTree().getSelectionModel().unselect(this);
16960     },
16961
16962     /**
16963      * Returns true if this node is selected
16964      * @return {Boolean}
16965      */
16966     isSelected : function(){
16967         return this.getOwnerTree().getSelectionModel().isSelected(this);
16968     },
16969
16970     /**
16971      * Expand this node.
16972      * @param {Boolean} deep (optional) True to expand all children as well
16973      * @param {Boolean} anim (optional) false to cancel the default animation
16974      * @param {Function} callback (optional) A callback to be called when
16975      * expanding this node completes (does not wait for deep expand to complete).
16976      * Called with 1 parameter, this node.
16977      */
16978     expand : function(deep, anim, callback){
16979         if(!this.expanded){
16980             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
16981                 return;
16982             }
16983             if(!this.childrenRendered){
16984                 this.renderChildren();
16985             }
16986             this.expanded = true;
16987             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
16988                 this.ui.animExpand(function(){
16989                     this.fireEvent("expand", this);
16990                     if(typeof callback == "function"){
16991                         callback(this);
16992                     }
16993                     if(deep === true){
16994                         this.expandChildNodes(true);
16995                     }
16996                 }.createDelegate(this));
16997                 return;
16998             }else{
16999                 this.ui.expand();
17000                 this.fireEvent("expand", this);
17001                 if(typeof callback == "function"){
17002                     callback(this);
17003                 }
17004             }
17005         }else{
17006            if(typeof callback == "function"){
17007                callback(this);
17008            }
17009         }
17010         if(deep === true){
17011             this.expandChildNodes(true);
17012         }
17013     },
17014
17015     isHiddenRoot : function(){
17016         return this.isRoot && !this.getOwnerTree().rootVisible;
17017     },
17018
17019     /**
17020      * Collapse this node.
17021      * @param {Boolean} deep (optional) True to collapse all children as well
17022      * @param {Boolean} anim (optional) false to cancel the default animation
17023      */
17024     collapse : function(deep, anim){
17025         if(this.expanded && !this.isHiddenRoot()){
17026             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17027                 return;
17028             }
17029             this.expanded = false;
17030             if((this.getOwnerTree().animate && anim !== false) || anim){
17031                 this.ui.animCollapse(function(){
17032                     this.fireEvent("collapse", this);
17033                     if(deep === true){
17034                         this.collapseChildNodes(true);
17035                     }
17036                 }.createDelegate(this));
17037                 return;
17038             }else{
17039                 this.ui.collapse();
17040                 this.fireEvent("collapse", this);
17041             }
17042         }
17043         if(deep === true){
17044             var cs = this.childNodes;
17045             for(var i = 0, len = cs.length; i < len; i++) {
17046                 cs[i].collapse(true, false);
17047             }
17048         }
17049     },
17050
17051     // private
17052     delayedExpand : function(delay){
17053         if(!this.expandProcId){
17054             this.expandProcId = this.expand.defer(delay, this);
17055         }
17056     },
17057
17058     // private
17059     cancelExpand : function(){
17060         if(this.expandProcId){
17061             clearTimeout(this.expandProcId);
17062         }
17063         this.expandProcId = false;
17064     },
17065
17066     /**
17067      * Toggles expanded/collapsed state of the node
17068      */
17069     toggle : function(){
17070         if(this.expanded){
17071             this.collapse();
17072         }else{
17073             this.expand();
17074         }
17075     },
17076
17077     /**
17078      * Ensures all parent nodes are expanded
17079      */
17080     ensureVisible : function(callback){
17081         var tree = this.getOwnerTree();
17082         tree.expandPath(this.parentNode.getPath(), false, function(){
17083             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17084             Roo.callback(callback);
17085         }.createDelegate(this));
17086     },
17087
17088     /**
17089      * Expand all child nodes
17090      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17091      */
17092     expandChildNodes : function(deep){
17093         var cs = this.childNodes;
17094         for(var i = 0, len = cs.length; i < len; i++) {
17095                 cs[i].expand(deep);
17096         }
17097     },
17098
17099     /**
17100      * Collapse all child nodes
17101      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17102      */
17103     collapseChildNodes : function(deep){
17104         var cs = this.childNodes;
17105         for(var i = 0, len = cs.length; i < len; i++) {
17106                 cs[i].collapse(deep);
17107         }
17108     },
17109
17110     /**
17111      * Disables this node
17112      */
17113     disable : function(){
17114         this.disabled = true;
17115         this.unselect();
17116         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17117             this.ui.onDisableChange(this, true);
17118         }
17119         this.fireEvent("disabledchange", this, true);
17120     },
17121
17122     /**
17123      * Enables this node
17124      */
17125     enable : function(){
17126         this.disabled = false;
17127         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17128             this.ui.onDisableChange(this, false);
17129         }
17130         this.fireEvent("disabledchange", this, false);
17131     },
17132
17133     // private
17134     renderChildren : function(suppressEvent){
17135         if(suppressEvent !== false){
17136             this.fireEvent("beforechildrenrendered", this);
17137         }
17138         var cs = this.childNodes;
17139         for(var i = 0, len = cs.length; i < len; i++){
17140             cs[i].render(true);
17141         }
17142         this.childrenRendered = true;
17143     },
17144
17145     // private
17146     sort : function(fn, scope){
17147         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17148         if(this.childrenRendered){
17149             var cs = this.childNodes;
17150             for(var i = 0, len = cs.length; i < len; i++){
17151                 cs[i].render(true);
17152             }
17153         }
17154     },
17155
17156     // private
17157     render : function(bulkRender){
17158         this.ui.render(bulkRender);
17159         if(!this.rendered){
17160             this.rendered = true;
17161             if(this.expanded){
17162                 this.expanded = false;
17163                 this.expand(false, false);
17164             }
17165         }
17166     },
17167
17168     // private
17169     renderIndent : function(deep, refresh){
17170         if(refresh){
17171             this.ui.childIndent = null;
17172         }
17173         this.ui.renderIndent();
17174         if(deep === true && this.childrenRendered){
17175             var cs = this.childNodes;
17176             for(var i = 0, len = cs.length; i < len; i++){
17177                 cs[i].renderIndent(true, refresh);
17178             }
17179         }
17180     }
17181 });/*
17182  * Based on:
17183  * Ext JS Library 1.1.1
17184  * Copyright(c) 2006-2007, Ext JS, LLC.
17185  *
17186  * Originally Released Under LGPL - original licence link has changed is not relivant.
17187  *
17188  * Fork - LGPL
17189  * <script type="text/javascript">
17190  */
17191  
17192 /**
17193  * @class Roo.tree.AsyncTreeNode
17194  * @extends Roo.tree.TreeNode
17195  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17196  * @constructor
17197  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17198  */
17199  Roo.tree.AsyncTreeNode = function(config){
17200     this.loaded = false;
17201     this.loading = false;
17202     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17203     /**
17204     * @event beforeload
17205     * Fires before this node is loaded, return false to cancel
17206     * @param {Node} this This node
17207     */
17208     this.addEvents({'beforeload':true, 'load': true});
17209     /**
17210     * @event load
17211     * Fires when this node is loaded
17212     * @param {Node} this This node
17213     */
17214     /**
17215      * The loader used by this node (defaults to using the tree's defined loader)
17216      * @type TreeLoader
17217      * @property loader
17218      */
17219 };
17220 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17221     expand : function(deep, anim, callback){
17222         if(this.loading){ // if an async load is already running, waiting til it's done
17223             var timer;
17224             var f = function(){
17225                 if(!this.loading){ // done loading
17226                     clearInterval(timer);
17227                     this.expand(deep, anim, callback);
17228                 }
17229             }.createDelegate(this);
17230             timer = setInterval(f, 200);
17231             return;
17232         }
17233         if(!this.loaded){
17234             if(this.fireEvent("beforeload", this) === false){
17235                 return;
17236             }
17237             this.loading = true;
17238             this.ui.beforeLoad(this);
17239             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17240             if(loader){
17241                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17242                 return;
17243             }
17244         }
17245         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17246     },
17247     
17248     /**
17249      * Returns true if this node is currently loading
17250      * @return {Boolean}
17251      */
17252     isLoading : function(){
17253         return this.loading;  
17254     },
17255     
17256     loadComplete : function(deep, anim, callback){
17257         this.loading = false;
17258         this.loaded = true;
17259         this.ui.afterLoad(this);
17260         this.fireEvent("load", this);
17261         this.expand(deep, anim, callback);
17262     },
17263     
17264     /**
17265      * Returns true if this node has been loaded
17266      * @return {Boolean}
17267      */
17268     isLoaded : function(){
17269         return this.loaded;
17270     },
17271     
17272     hasChildNodes : function(){
17273         if(!this.isLeaf() && !this.loaded){
17274             return true;
17275         }else{
17276             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17277         }
17278     },
17279
17280     /**
17281      * Trigger a reload for this node
17282      * @param {Function} callback
17283      */
17284     reload : function(callback){
17285         this.collapse(false, false);
17286         while(this.firstChild){
17287             this.removeChild(this.firstChild);
17288         }
17289         this.childrenRendered = false;
17290         this.loaded = false;
17291         if(this.isHiddenRoot()){
17292             this.expanded = false;
17293         }
17294         this.expand(false, false, callback);
17295     }
17296 });/*
17297  * Based on:
17298  * Ext JS Library 1.1.1
17299  * Copyright(c) 2006-2007, Ext JS, LLC.
17300  *
17301  * Originally Released Under LGPL - original licence link has changed is not relivant.
17302  *
17303  * Fork - LGPL
17304  * <script type="text/javascript">
17305  */
17306  
17307 /**
17308  * @class Roo.tree.TreeNodeUI
17309  * @constructor
17310  * @param {Object} node The node to render
17311  * The TreeNode UI implementation is separate from the
17312  * tree implementation. Unless you are customizing the tree UI,
17313  * you should never have to use this directly.
17314  */
17315 Roo.tree.TreeNodeUI = function(node){
17316     this.node = node;
17317     this.rendered = false;
17318     this.animating = false;
17319     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17320 };
17321
17322 Roo.tree.TreeNodeUI.prototype = {
17323     removeChild : function(node){
17324         if(this.rendered){
17325             this.ctNode.removeChild(node.ui.getEl());
17326         }
17327     },
17328
17329     beforeLoad : function(){
17330          this.addClass("x-tree-node-loading");
17331     },
17332
17333     afterLoad : function(){
17334          this.removeClass("x-tree-node-loading");
17335     },
17336
17337     onTextChange : function(node, text, oldText){
17338         if(this.rendered){
17339             this.textNode.innerHTML = text;
17340         }
17341     },
17342
17343     onDisableChange : function(node, state){
17344         this.disabled = state;
17345         if(state){
17346             this.addClass("x-tree-node-disabled");
17347         }else{
17348             this.removeClass("x-tree-node-disabled");
17349         }
17350     },
17351
17352     onSelectedChange : function(state){
17353         if(state){
17354             this.focus();
17355             this.addClass("x-tree-selected");
17356         }else{
17357             //this.blur();
17358             this.removeClass("x-tree-selected");
17359         }
17360     },
17361
17362     onMove : function(tree, node, oldParent, newParent, index, refNode){
17363         this.childIndent = null;
17364         if(this.rendered){
17365             var targetNode = newParent.ui.getContainer();
17366             if(!targetNode){//target not rendered
17367                 this.holder = document.createElement("div");
17368                 this.holder.appendChild(this.wrap);
17369                 return;
17370             }
17371             var insertBefore = refNode ? refNode.ui.getEl() : null;
17372             if(insertBefore){
17373                 targetNode.insertBefore(this.wrap, insertBefore);
17374             }else{
17375                 targetNode.appendChild(this.wrap);
17376             }
17377             this.node.renderIndent(true);
17378         }
17379     },
17380
17381     addClass : function(cls){
17382         if(this.elNode){
17383             Roo.fly(this.elNode).addClass(cls);
17384         }
17385     },
17386
17387     removeClass : function(cls){
17388         if(this.elNode){
17389             Roo.fly(this.elNode).removeClass(cls);
17390         }
17391     },
17392
17393     remove : function(){
17394         if(this.rendered){
17395             this.holder = document.createElement("div");
17396             this.holder.appendChild(this.wrap);
17397         }
17398     },
17399
17400     fireEvent : function(){
17401         return this.node.fireEvent.apply(this.node, arguments);
17402     },
17403
17404     initEvents : function(){
17405         this.node.on("move", this.onMove, this);
17406         var E = Roo.EventManager;
17407         var a = this.anchor;
17408
17409         var el = Roo.fly(a, '_treeui');
17410
17411         if(Roo.isOpera){ // opera render bug ignores the CSS
17412             el.setStyle("text-decoration", "none");
17413         }
17414
17415         el.on("click", this.onClick, this);
17416         el.on("dblclick", this.onDblClick, this);
17417
17418         if(this.checkbox){
17419             Roo.EventManager.on(this.checkbox,
17420                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17421         }
17422
17423         el.on("contextmenu", this.onContextMenu, this);
17424
17425         var icon = Roo.fly(this.iconNode);
17426         icon.on("click", this.onClick, this);
17427         icon.on("dblclick", this.onDblClick, this);
17428         icon.on("contextmenu", this.onContextMenu, this);
17429         E.on(this.ecNode, "click", this.ecClick, this, true);
17430
17431         if(this.node.disabled){
17432             this.addClass("x-tree-node-disabled");
17433         }
17434         if(this.node.hidden){
17435             this.addClass("x-tree-node-disabled");
17436         }
17437         var ot = this.node.getOwnerTree();
17438         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17439         if(dd && (!this.node.isRoot || ot.rootVisible)){
17440             Roo.dd.Registry.register(this.elNode, {
17441                 node: this.node,
17442                 handles: this.getDDHandles(),
17443                 isHandle: false
17444             });
17445         }
17446     },
17447
17448     getDDHandles : function(){
17449         return [this.iconNode, this.textNode];
17450     },
17451
17452     hide : function(){
17453         if(this.rendered){
17454             this.wrap.style.display = "none";
17455         }
17456     },
17457
17458     show : function(){
17459         if(this.rendered){
17460             this.wrap.style.display = "";
17461         }
17462     },
17463
17464     onContextMenu : function(e){
17465         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17466             e.preventDefault();
17467             this.focus();
17468             this.fireEvent("contextmenu", this.node, e);
17469         }
17470     },
17471
17472     onClick : function(e){
17473         if(this.dropping){
17474             e.stopEvent();
17475             return;
17476         }
17477         if(this.fireEvent("beforeclick", this.node, e) !== false){
17478             if(!this.disabled && this.node.attributes.href){
17479                 this.fireEvent("click", this.node, e);
17480                 return;
17481             }
17482             e.preventDefault();
17483             if(this.disabled){
17484                 return;
17485             }
17486
17487             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17488                 this.node.toggle();
17489             }
17490
17491             this.fireEvent("click", this.node, e);
17492         }else{
17493             e.stopEvent();
17494         }
17495     },
17496
17497     onDblClick : function(e){
17498         e.preventDefault();
17499         if(this.disabled){
17500             return;
17501         }
17502         if(this.checkbox){
17503             this.toggleCheck();
17504         }
17505         if(!this.animating && this.node.hasChildNodes()){
17506             this.node.toggle();
17507         }
17508         this.fireEvent("dblclick", this.node, e);
17509     },
17510
17511     onCheckChange : function(){
17512         var checked = this.checkbox.checked;
17513         this.node.attributes.checked = checked;
17514         this.fireEvent('checkchange', this.node, checked);
17515     },
17516
17517     ecClick : function(e){
17518         if(!this.animating && this.node.hasChildNodes()){
17519             this.node.toggle();
17520         }
17521     },
17522
17523     startDrop : function(){
17524         this.dropping = true;
17525     },
17526
17527     // delayed drop so the click event doesn't get fired on a drop
17528     endDrop : function(){
17529        setTimeout(function(){
17530            this.dropping = false;
17531        }.createDelegate(this), 50);
17532     },
17533
17534     expand : function(){
17535         this.updateExpandIcon();
17536         this.ctNode.style.display = "";
17537     },
17538
17539     focus : function(){
17540         if(!this.node.preventHScroll){
17541             try{this.anchor.focus();
17542             }catch(e){}
17543         }else if(!Roo.isIE){
17544             try{
17545                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17546                 var l = noscroll.scrollLeft;
17547                 this.anchor.focus();
17548                 noscroll.scrollLeft = l;
17549             }catch(e){}
17550         }
17551     },
17552
17553     toggleCheck : function(value){
17554         var cb = this.checkbox;
17555         if(cb){
17556             cb.checked = (value === undefined ? !cb.checked : value);
17557         }
17558     },
17559
17560     blur : function(){
17561         try{
17562             this.anchor.blur();
17563         }catch(e){}
17564     },
17565
17566     animExpand : function(callback){
17567         var ct = Roo.get(this.ctNode);
17568         ct.stopFx();
17569         if(!this.node.hasChildNodes()){
17570             this.updateExpandIcon();
17571             this.ctNode.style.display = "";
17572             Roo.callback(callback);
17573             return;
17574         }
17575         this.animating = true;
17576         this.updateExpandIcon();
17577
17578         ct.slideIn('t', {
17579            callback : function(){
17580                this.animating = false;
17581                Roo.callback(callback);
17582             },
17583             scope: this,
17584             duration: this.node.ownerTree.duration || .25
17585         });
17586     },
17587
17588     highlight : function(){
17589         var tree = this.node.getOwnerTree();
17590         Roo.fly(this.wrap).highlight(
17591             tree.hlColor || "C3DAF9",
17592             {endColor: tree.hlBaseColor}
17593         );
17594     },
17595
17596     collapse : function(){
17597         this.updateExpandIcon();
17598         this.ctNode.style.display = "none";
17599     },
17600
17601     animCollapse : function(callback){
17602         var ct = Roo.get(this.ctNode);
17603         ct.enableDisplayMode('block');
17604         ct.stopFx();
17605
17606         this.animating = true;
17607         this.updateExpandIcon();
17608
17609         ct.slideOut('t', {
17610             callback : function(){
17611                this.animating = false;
17612                Roo.callback(callback);
17613             },
17614             scope: this,
17615             duration: this.node.ownerTree.duration || .25
17616         });
17617     },
17618
17619     getContainer : function(){
17620         return this.ctNode;
17621     },
17622
17623     getEl : function(){
17624         return this.wrap;
17625     },
17626
17627     appendDDGhost : function(ghostNode){
17628         ghostNode.appendChild(this.elNode.cloneNode(true));
17629     },
17630
17631     getDDRepairXY : function(){
17632         return Roo.lib.Dom.getXY(this.iconNode);
17633     },
17634
17635     onRender : function(){
17636         this.render();
17637     },
17638
17639     render : function(bulkRender){
17640         var n = this.node, a = n.attributes;
17641         var targetNode = n.parentNode ?
17642               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17643
17644         if(!this.rendered){
17645             this.rendered = true;
17646
17647             this.renderElements(n, a, targetNode, bulkRender);
17648
17649             if(a.qtip){
17650                if(this.textNode.setAttributeNS){
17651                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17652                    if(a.qtipTitle){
17653                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17654                    }
17655                }else{
17656                    this.textNode.setAttribute("ext:qtip", a.qtip);
17657                    if(a.qtipTitle){
17658                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17659                    }
17660                }
17661             }else if(a.qtipCfg){
17662                 a.qtipCfg.target = Roo.id(this.textNode);
17663                 Roo.QuickTips.register(a.qtipCfg);
17664             }
17665             this.initEvents();
17666             if(!this.node.expanded){
17667                 this.updateExpandIcon();
17668             }
17669         }else{
17670             if(bulkRender === true) {
17671                 targetNode.appendChild(this.wrap);
17672             }
17673         }
17674     },
17675
17676     renderElements : function(n, a, targetNode, bulkRender)
17677     {
17678         // add some indent caching, this helps performance when rendering a large tree
17679         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17680         var t = n.getOwnerTree();
17681         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17682         if (typeof(n.attributes.html) != 'undefined') {
17683             txt = n.attributes.html;
17684         }
17685         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17686         var cb = typeof a.checked == 'boolean';
17687         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17688         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17689             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17690             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17691             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17692             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17693             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17694              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17695                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17696             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17697             "</li>"];
17698
17699         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17700             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17701                                 n.nextSibling.ui.getEl(), buf.join(""));
17702         }else{
17703             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17704         }
17705
17706         this.elNode = this.wrap.childNodes[0];
17707         this.ctNode = this.wrap.childNodes[1];
17708         var cs = this.elNode.childNodes;
17709         this.indentNode = cs[0];
17710         this.ecNode = cs[1];
17711         this.iconNode = cs[2];
17712         var index = 3;
17713         if(cb){
17714             this.checkbox = cs[3];
17715             index++;
17716         }
17717         this.anchor = cs[index];
17718         this.textNode = cs[index].firstChild;
17719     },
17720
17721     getAnchor : function(){
17722         return this.anchor;
17723     },
17724
17725     getTextEl : function(){
17726         return this.textNode;
17727     },
17728
17729     getIconEl : function(){
17730         return this.iconNode;
17731     },
17732
17733     isChecked : function(){
17734         return this.checkbox ? this.checkbox.checked : false;
17735     },
17736
17737     updateExpandIcon : function(){
17738         if(this.rendered){
17739             var n = this.node, c1, c2;
17740             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17741             var hasChild = n.hasChildNodes();
17742             if(hasChild){
17743                 if(n.expanded){
17744                     cls += "-minus";
17745                     c1 = "x-tree-node-collapsed";
17746                     c2 = "x-tree-node-expanded";
17747                 }else{
17748                     cls += "-plus";
17749                     c1 = "x-tree-node-expanded";
17750                     c2 = "x-tree-node-collapsed";
17751                 }
17752                 if(this.wasLeaf){
17753                     this.removeClass("x-tree-node-leaf");
17754                     this.wasLeaf = false;
17755                 }
17756                 if(this.c1 != c1 || this.c2 != c2){
17757                     Roo.fly(this.elNode).replaceClass(c1, c2);
17758                     this.c1 = c1; this.c2 = c2;
17759                 }
17760             }else{
17761                 // this changes non-leafs into leafs if they have no children.
17762                 // it's not very rational behaviour..
17763                 
17764                 if(!this.wasLeaf && this.node.leaf){
17765                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17766                     delete this.c1;
17767                     delete this.c2;
17768                     this.wasLeaf = true;
17769                 }
17770             }
17771             var ecc = "x-tree-ec-icon "+cls;
17772             if(this.ecc != ecc){
17773                 this.ecNode.className = ecc;
17774                 this.ecc = ecc;
17775             }
17776         }
17777     },
17778
17779     getChildIndent : function(){
17780         if(!this.childIndent){
17781             var buf = [];
17782             var p = this.node;
17783             while(p){
17784                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17785                     if(!p.isLast()) {
17786                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17787                     } else {
17788                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17789                     }
17790                 }
17791                 p = p.parentNode;
17792             }
17793             this.childIndent = buf.join("");
17794         }
17795         return this.childIndent;
17796     },
17797
17798     renderIndent : function(){
17799         if(this.rendered){
17800             var indent = "";
17801             var p = this.node.parentNode;
17802             if(p){
17803                 indent = p.ui.getChildIndent();
17804             }
17805             if(this.indentMarkup != indent){ // don't rerender if not required
17806                 this.indentNode.innerHTML = indent;
17807                 this.indentMarkup = indent;
17808             }
17809             this.updateExpandIcon();
17810         }
17811     }
17812 };
17813
17814 Roo.tree.RootTreeNodeUI = function(){
17815     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17816 };
17817 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17818     render : function(){
17819         if(!this.rendered){
17820             var targetNode = this.node.ownerTree.innerCt.dom;
17821             this.node.expanded = true;
17822             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17823             this.wrap = this.ctNode = targetNode.firstChild;
17824         }
17825     },
17826     collapse : function(){
17827     },
17828     expand : function(){
17829     }
17830 });/*
17831  * Based on:
17832  * Ext JS Library 1.1.1
17833  * Copyright(c) 2006-2007, Ext JS, LLC.
17834  *
17835  * Originally Released Under LGPL - original licence link has changed is not relivant.
17836  *
17837  * Fork - LGPL
17838  * <script type="text/javascript">
17839  */
17840 /**
17841  * @class Roo.tree.TreeLoader
17842  * @extends Roo.util.Observable
17843  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17844  * nodes from a specified URL. The response must be a javascript Array definition
17845  * who's elements are node definition objects. eg:
17846  * <pre><code>
17847 {  success : true,
17848    data :      [
17849    
17850     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17851     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17852     ]
17853 }
17854
17855
17856 </code></pre>
17857  * <br><br>
17858  * The old style respose with just an array is still supported, but not recommended.
17859  * <br><br>
17860  *
17861  * A server request is sent, and child nodes are loaded only when a node is expanded.
17862  * The loading node's id is passed to the server under the parameter name "node" to
17863  * enable the server to produce the correct child nodes.
17864  * <br><br>
17865  * To pass extra parameters, an event handler may be attached to the "beforeload"
17866  * event, and the parameters specified in the TreeLoader's baseParams property:
17867  * <pre><code>
17868     myTreeLoader.on("beforeload", function(treeLoader, node) {
17869         this.baseParams.category = node.attributes.category;
17870     }, this);
17871 </code></pre><
17872  * This would pass an HTTP parameter called "category" to the server containing
17873  * the value of the Node's "category" attribute.
17874  * @constructor
17875  * Creates a new Treeloader.
17876  * @param {Object} config A config object containing config properties.
17877  */
17878 Roo.tree.TreeLoader = function(config){
17879     this.baseParams = {};
17880     this.requestMethod = "POST";
17881     Roo.apply(this, config);
17882
17883     this.addEvents({
17884     
17885         /**
17886          * @event beforeload
17887          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17888          * @param {Object} This TreeLoader object.
17889          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17890          * @param {Object} callback The callback function specified in the {@link #load} call.
17891          */
17892         beforeload : true,
17893         /**
17894          * @event load
17895          * Fires when the node has been successfuly loaded.
17896          * @param {Object} This TreeLoader object.
17897          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17898          * @param {Object} response The response object containing the data from the server.
17899          */
17900         load : true,
17901         /**
17902          * @event loadexception
17903          * Fires if the network request failed.
17904          * @param {Object} This TreeLoader object.
17905          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17906          * @param {Object} response The response object containing the data from the server.
17907          */
17908         loadexception : true,
17909         /**
17910          * @event create
17911          * Fires before a node is created, enabling you to return custom Node types 
17912          * @param {Object} This TreeLoader object.
17913          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17914          */
17915         create : true
17916     });
17917
17918     Roo.tree.TreeLoader.superclass.constructor.call(this);
17919 };
17920
17921 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17922     /**
17923     * @cfg {String} dataUrl The URL from which to request a Json string which
17924     * specifies an array of node definition object representing the child nodes
17925     * to be loaded.
17926     */
17927     /**
17928     * @cfg {String} requestMethod either GET or POST
17929     * defaults to POST (due to BC)
17930     * to be loaded.
17931     */
17932     /**
17933     * @cfg {Object} baseParams (optional) An object containing properties which
17934     * specify HTTP parameters to be passed to each request for child nodes.
17935     */
17936     /**
17937     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17938     * created by this loader. If the attributes sent by the server have an attribute in this object,
17939     * they take priority.
17940     */
17941     /**
17942     * @cfg {Object} uiProviders (optional) An object containing properties which
17943     * 
17944     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17945     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17946     * <i>uiProvider</i> attribute of a returned child node is a string rather
17947     * than a reference to a TreeNodeUI implementation, this that string value
17948     * is used as a property name in the uiProviders object. You can define the provider named
17949     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17950     */
17951     uiProviders : {},
17952
17953     /**
17954     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17955     * child nodes before loading.
17956     */
17957     clearOnLoad : true,
17958
17959     /**
17960     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
17961     * property on loading, rather than expecting an array. (eg. more compatible to a standard
17962     * Grid query { data : [ .....] }
17963     */
17964     
17965     root : false,
17966      /**
17967     * @cfg {String} queryParam (optional) 
17968     * Name of the query as it will be passed on the querystring (defaults to 'node')
17969     * eg. the request will be ?node=[id]
17970     */
17971     
17972     
17973     queryParam: false,
17974     
17975     /**
17976      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
17977      * This is called automatically when a node is expanded, but may be used to reload
17978      * a node (or append new children if the {@link #clearOnLoad} option is false.)
17979      * @param {Roo.tree.TreeNode} node
17980      * @param {Function} callback
17981      */
17982     load : function(node, callback){
17983         if(this.clearOnLoad){
17984             while(node.firstChild){
17985                 node.removeChild(node.firstChild);
17986             }
17987         }
17988         if(node.attributes.children){ // preloaded json children
17989             var cs = node.attributes.children;
17990             for(var i = 0, len = cs.length; i < len; i++){
17991                 node.appendChild(this.createNode(cs[i]));
17992             }
17993             if(typeof callback == "function"){
17994                 callback();
17995             }
17996         }else if(this.dataUrl){
17997             this.requestData(node, callback);
17998         }
17999     },
18000
18001     getParams: function(node){
18002         var buf = [], bp = this.baseParams;
18003         for(var key in bp){
18004             if(typeof bp[key] != "function"){
18005                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18006             }
18007         }
18008         var n = this.queryParam === false ? 'node' : this.queryParam;
18009         buf.push(n + "=", encodeURIComponent(node.id));
18010         return buf.join("");
18011     },
18012
18013     requestData : function(node, callback){
18014         if(this.fireEvent("beforeload", this, node, callback) !== false){
18015             this.transId = Roo.Ajax.request({
18016                 method:this.requestMethod,
18017                 url: this.dataUrl||this.url,
18018                 success: this.handleResponse,
18019                 failure: this.handleFailure,
18020                 scope: this,
18021                 argument: {callback: callback, node: node},
18022                 params: this.getParams(node)
18023             });
18024         }else{
18025             // if the load is cancelled, make sure we notify
18026             // the node that we are done
18027             if(typeof callback == "function"){
18028                 callback();
18029             }
18030         }
18031     },
18032
18033     isLoading : function(){
18034         return this.transId ? true : false;
18035     },
18036
18037     abort : function(){
18038         if(this.isLoading()){
18039             Roo.Ajax.abort(this.transId);
18040         }
18041     },
18042
18043     // private
18044     createNode : function(attr)
18045     {
18046         // apply baseAttrs, nice idea Corey!
18047         if(this.baseAttrs){
18048             Roo.applyIf(attr, this.baseAttrs);
18049         }
18050         if(this.applyLoader !== false){
18051             attr.loader = this;
18052         }
18053         // uiProvider = depreciated..
18054         
18055         if(typeof(attr.uiProvider) == 'string'){
18056            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18057                 /**  eval:var:attr */ eval(attr.uiProvider);
18058         }
18059         if(typeof(this.uiProviders['default']) != 'undefined') {
18060             attr.uiProvider = this.uiProviders['default'];
18061         }
18062         
18063         this.fireEvent('create', this, attr);
18064         
18065         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18066         return(attr.leaf ?
18067                         new Roo.tree.TreeNode(attr) :
18068                         new Roo.tree.AsyncTreeNode(attr));
18069     },
18070
18071     processResponse : function(response, node, callback)
18072     {
18073         var json = response.responseText;
18074         try {
18075             
18076             var o = Roo.decode(json);
18077             
18078             if (this.root === false && typeof(o.success) != undefined) {
18079                 this.root = 'data'; // the default behaviour for list like data..
18080                 }
18081                 
18082             if (this.root !== false &&  !o.success) {
18083                 // it's a failure condition.
18084                 var a = response.argument;
18085                 this.fireEvent("loadexception", this, a.node, response);
18086                 Roo.log("Load failed - should have a handler really");
18087                 return;
18088             }
18089             
18090             
18091             
18092             if (this.root !== false) {
18093                  o = o[this.root];
18094             }
18095             
18096             for(var i = 0, len = o.length; i < len; i++){
18097                 var n = this.createNode(o[i]);
18098                 if(n){
18099                     node.appendChild(n);
18100                 }
18101             }
18102             if(typeof callback == "function"){
18103                 callback(this, node);
18104             }
18105         }catch(e){
18106             this.handleFailure(response);
18107         }
18108     },
18109
18110     handleResponse : function(response){
18111         this.transId = false;
18112         var a = response.argument;
18113         this.processResponse(response, a.node, a.callback);
18114         this.fireEvent("load", this, a.node, response);
18115     },
18116
18117     handleFailure : function(response)
18118     {
18119         // should handle failure better..
18120         this.transId = false;
18121         var a = response.argument;
18122         this.fireEvent("loadexception", this, a.node, response);
18123         if(typeof a.callback == "function"){
18124             a.callback(this, a.node);
18125         }
18126     }
18127 });/*
18128  * Based on:
18129  * Ext JS Library 1.1.1
18130  * Copyright(c) 2006-2007, Ext JS, LLC.
18131  *
18132  * Originally Released Under LGPL - original licence link has changed is not relivant.
18133  *
18134  * Fork - LGPL
18135  * <script type="text/javascript">
18136  */
18137
18138 /**
18139 * @class Roo.tree.TreeFilter
18140 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18141 * @param {TreePanel} tree
18142 * @param {Object} config (optional)
18143  */
18144 Roo.tree.TreeFilter = function(tree, config){
18145     this.tree = tree;
18146     this.filtered = {};
18147     Roo.apply(this, config);
18148 };
18149
18150 Roo.tree.TreeFilter.prototype = {
18151     clearBlank:false,
18152     reverse:false,
18153     autoClear:false,
18154     remove:false,
18155
18156      /**
18157      * Filter the data by a specific attribute.
18158      * @param {String/RegExp} value Either string that the attribute value
18159      * should start with or a RegExp to test against the attribute
18160      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18161      * @param {TreeNode} startNode (optional) The node to start the filter at.
18162      */
18163     filter : function(value, attr, startNode){
18164         attr = attr || "text";
18165         var f;
18166         if(typeof value == "string"){
18167             var vlen = value.length;
18168             // auto clear empty filter
18169             if(vlen == 0 && this.clearBlank){
18170                 this.clear();
18171                 return;
18172             }
18173             value = value.toLowerCase();
18174             f = function(n){
18175                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18176             };
18177         }else if(value.exec){ // regex?
18178             f = function(n){
18179                 return value.test(n.attributes[attr]);
18180             };
18181         }else{
18182             throw 'Illegal filter type, must be string or regex';
18183         }
18184         this.filterBy(f, null, startNode);
18185         },
18186
18187     /**
18188      * Filter by a function. The passed function will be called with each
18189      * node in the tree (or from the startNode). If the function returns true, the node is kept
18190      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18191      * @param {Function} fn The filter function
18192      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18193      */
18194     filterBy : function(fn, scope, startNode){
18195         startNode = startNode || this.tree.root;
18196         if(this.autoClear){
18197             this.clear();
18198         }
18199         var af = this.filtered, rv = this.reverse;
18200         var f = function(n){
18201             if(n == startNode){
18202                 return true;
18203             }
18204             if(af[n.id]){
18205                 return false;
18206             }
18207             var m = fn.call(scope || n, n);
18208             if(!m || rv){
18209                 af[n.id] = n;
18210                 n.ui.hide();
18211                 return false;
18212             }
18213             return true;
18214         };
18215         startNode.cascade(f);
18216         if(this.remove){
18217            for(var id in af){
18218                if(typeof id != "function"){
18219                    var n = af[id];
18220                    if(n && n.parentNode){
18221                        n.parentNode.removeChild(n);
18222                    }
18223                }
18224            }
18225         }
18226     },
18227
18228     /**
18229      * Clears the current filter. Note: with the "remove" option
18230      * set a filter cannot be cleared.
18231      */
18232     clear : function(){
18233         var t = this.tree;
18234         var af = this.filtered;
18235         for(var id in af){
18236             if(typeof id != "function"){
18237                 var n = af[id];
18238                 if(n){
18239                     n.ui.show();
18240                 }
18241             }
18242         }
18243         this.filtered = {};
18244     }
18245 };
18246 /*
18247  * Based on:
18248  * Ext JS Library 1.1.1
18249  * Copyright(c) 2006-2007, Ext JS, LLC.
18250  *
18251  * Originally Released Under LGPL - original licence link has changed is not relivant.
18252  *
18253  * Fork - LGPL
18254  * <script type="text/javascript">
18255  */
18256  
18257
18258 /**
18259  * @class Roo.tree.TreeSorter
18260  * Provides sorting of nodes in a TreePanel
18261  * 
18262  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18263  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18264  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18265  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18266  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18267  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18268  * @constructor
18269  * @param {TreePanel} tree
18270  * @param {Object} config
18271  */
18272 Roo.tree.TreeSorter = function(tree, config){
18273     Roo.apply(this, config);
18274     tree.on("beforechildrenrendered", this.doSort, this);
18275     tree.on("append", this.updateSort, this);
18276     tree.on("insert", this.updateSort, this);
18277     
18278     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18279     var p = this.property || "text";
18280     var sortType = this.sortType;
18281     var fs = this.folderSort;
18282     var cs = this.caseSensitive === true;
18283     var leafAttr = this.leafAttr || 'leaf';
18284
18285     this.sortFn = function(n1, n2){
18286         if(fs){
18287             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18288                 return 1;
18289             }
18290             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18291                 return -1;
18292             }
18293         }
18294         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18295         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18296         if(v1 < v2){
18297                         return dsc ? +1 : -1;
18298                 }else if(v1 > v2){
18299                         return dsc ? -1 : +1;
18300         }else{
18301                 return 0;
18302         }
18303     };
18304 };
18305
18306 Roo.tree.TreeSorter.prototype = {
18307     doSort : function(node){
18308         node.sort(this.sortFn);
18309     },
18310     
18311     compareNodes : function(n1, n2){
18312         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18313     },
18314     
18315     updateSort : function(tree, node){
18316         if(node.childrenRendered){
18317             this.doSort.defer(1, this, [node]);
18318         }
18319     }
18320 };/*
18321  * Based on:
18322  * Ext JS Library 1.1.1
18323  * Copyright(c) 2006-2007, Ext JS, LLC.
18324  *
18325  * Originally Released Under LGPL - original licence link has changed is not relivant.
18326  *
18327  * Fork - LGPL
18328  * <script type="text/javascript">
18329  */
18330
18331 if(Roo.dd.DropZone){
18332     
18333 Roo.tree.TreeDropZone = function(tree, config){
18334     this.allowParentInsert = false;
18335     this.allowContainerDrop = false;
18336     this.appendOnly = false;
18337     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18338     this.tree = tree;
18339     this.lastInsertClass = "x-tree-no-status";
18340     this.dragOverData = {};
18341 };
18342
18343 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18344     ddGroup : "TreeDD",
18345     scroll:  true,
18346     
18347     expandDelay : 1000,
18348     
18349     expandNode : function(node){
18350         if(node.hasChildNodes() && !node.isExpanded()){
18351             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18352         }
18353     },
18354     
18355     queueExpand : function(node){
18356         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18357     },
18358     
18359     cancelExpand : function(){
18360         if(this.expandProcId){
18361             clearTimeout(this.expandProcId);
18362             this.expandProcId = false;
18363         }
18364     },
18365     
18366     isValidDropPoint : function(n, pt, dd, e, data){
18367         if(!n || !data){ return false; }
18368         var targetNode = n.node;
18369         var dropNode = data.node;
18370         // default drop rules
18371         if(!(targetNode && targetNode.isTarget && pt)){
18372             return false;
18373         }
18374         if(pt == "append" && targetNode.allowChildren === false){
18375             return false;
18376         }
18377         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18378             return false;
18379         }
18380         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18381             return false;
18382         }
18383         // reuse the object
18384         var overEvent = this.dragOverData;
18385         overEvent.tree = this.tree;
18386         overEvent.target = targetNode;
18387         overEvent.data = data;
18388         overEvent.point = pt;
18389         overEvent.source = dd;
18390         overEvent.rawEvent = e;
18391         overEvent.dropNode = dropNode;
18392         overEvent.cancel = false;  
18393         var result = this.tree.fireEvent("nodedragover", overEvent);
18394         return overEvent.cancel === false && result !== false;
18395     },
18396     
18397     getDropPoint : function(e, n, dd)
18398     {
18399         var tn = n.node;
18400         if(tn.isRoot){
18401             return tn.allowChildren !== false ? "append" : false; // always append for root
18402         }
18403         var dragEl = n.ddel;
18404         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18405         var y = Roo.lib.Event.getPageY(e);
18406         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18407         
18408         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18409         var noAppend = tn.allowChildren === false;
18410         if(this.appendOnly || tn.parentNode.allowChildren === false){
18411             return noAppend ? false : "append";
18412         }
18413         var noBelow = false;
18414         if(!this.allowParentInsert){
18415             noBelow = tn.hasChildNodes() && tn.isExpanded();
18416         }
18417         var q = (b - t) / (noAppend ? 2 : 3);
18418         if(y >= t && y < (t + q)){
18419             return "above";
18420         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18421             return "below";
18422         }else{
18423             return "append";
18424         }
18425     },
18426     
18427     onNodeEnter : function(n, dd, e, data)
18428     {
18429         this.cancelExpand();
18430     },
18431     
18432     onNodeOver : function(n, dd, e, data)
18433     {
18434        
18435         var pt = this.getDropPoint(e, n, dd);
18436         var node = n.node;
18437         
18438         // auto node expand check
18439         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18440             this.queueExpand(node);
18441         }else if(pt != "append"){
18442             this.cancelExpand();
18443         }
18444         
18445         // set the insert point style on the target node
18446         var returnCls = this.dropNotAllowed;
18447         if(this.isValidDropPoint(n, pt, dd, e, data)){
18448            if(pt){
18449                var el = n.ddel;
18450                var cls;
18451                if(pt == "above"){
18452                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18453                    cls = "x-tree-drag-insert-above";
18454                }else if(pt == "below"){
18455                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18456                    cls = "x-tree-drag-insert-below";
18457                }else{
18458                    returnCls = "x-tree-drop-ok-append";
18459                    cls = "x-tree-drag-append";
18460                }
18461                if(this.lastInsertClass != cls){
18462                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18463                    this.lastInsertClass = cls;
18464                }
18465            }
18466        }
18467        return returnCls;
18468     },
18469     
18470     onNodeOut : function(n, dd, e, data){
18471         
18472         this.cancelExpand();
18473         this.removeDropIndicators(n);
18474     },
18475     
18476     onNodeDrop : function(n, dd, e, data){
18477         var point = this.getDropPoint(e, n, dd);
18478         var targetNode = n.node;
18479         targetNode.ui.startDrop();
18480         if(!this.isValidDropPoint(n, point, dd, e, data)){
18481             targetNode.ui.endDrop();
18482             return false;
18483         }
18484         // first try to find the drop node
18485         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18486         var dropEvent = {
18487             tree : this.tree,
18488             target: targetNode,
18489             data: data,
18490             point: point,
18491             source: dd,
18492             rawEvent: e,
18493             dropNode: dropNode,
18494             cancel: !dropNode   
18495         };
18496         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18497         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18498             targetNode.ui.endDrop();
18499             return false;
18500         }
18501         // allow target changing
18502         targetNode = dropEvent.target;
18503         if(point == "append" && !targetNode.isExpanded()){
18504             targetNode.expand(false, null, function(){
18505                 this.completeDrop(dropEvent);
18506             }.createDelegate(this));
18507         }else{
18508             this.completeDrop(dropEvent);
18509         }
18510         return true;
18511     },
18512     
18513     completeDrop : function(de){
18514         var ns = de.dropNode, p = de.point, t = de.target;
18515         if(!(ns instanceof Array)){
18516             ns = [ns];
18517         }
18518         var n;
18519         for(var i = 0, len = ns.length; i < len; i++){
18520             n = ns[i];
18521             if(p == "above"){
18522                 t.parentNode.insertBefore(n, t);
18523             }else if(p == "below"){
18524                 t.parentNode.insertBefore(n, t.nextSibling);
18525             }else{
18526                 t.appendChild(n);
18527             }
18528         }
18529         n.ui.focus();
18530         if(this.tree.hlDrop){
18531             n.ui.highlight();
18532         }
18533         t.ui.endDrop();
18534         this.tree.fireEvent("nodedrop", de);
18535     },
18536     
18537     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18538         if(this.tree.hlDrop){
18539             dropNode.ui.focus();
18540             dropNode.ui.highlight();
18541         }
18542         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18543     },
18544     
18545     getTree : function(){
18546         return this.tree;
18547     },
18548     
18549     removeDropIndicators : function(n){
18550         if(n && n.ddel){
18551             var el = n.ddel;
18552             Roo.fly(el).removeClass([
18553                     "x-tree-drag-insert-above",
18554                     "x-tree-drag-insert-below",
18555                     "x-tree-drag-append"]);
18556             this.lastInsertClass = "_noclass";
18557         }
18558     },
18559     
18560     beforeDragDrop : function(target, e, id){
18561         this.cancelExpand();
18562         return true;
18563     },
18564     
18565     afterRepair : function(data){
18566         if(data && Roo.enableFx){
18567             data.node.ui.highlight();
18568         }
18569         this.hideProxy();
18570     } 
18571     
18572 });
18573
18574 }
18575 /*
18576  * Based on:
18577  * Ext JS Library 1.1.1
18578  * Copyright(c) 2006-2007, Ext JS, LLC.
18579  *
18580  * Originally Released Under LGPL - original licence link has changed is not relivant.
18581  *
18582  * Fork - LGPL
18583  * <script type="text/javascript">
18584  */
18585  
18586
18587 if(Roo.dd.DragZone){
18588 Roo.tree.TreeDragZone = function(tree, config){
18589     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18590     this.tree = tree;
18591 };
18592
18593 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18594     ddGroup : "TreeDD",
18595    
18596     onBeforeDrag : function(data, e){
18597         var n = data.node;
18598         return n && n.draggable && !n.disabled;
18599     },
18600      
18601     
18602     onInitDrag : function(e){
18603         var data = this.dragData;
18604         this.tree.getSelectionModel().select(data.node);
18605         this.proxy.update("");
18606         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18607         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18608     },
18609     
18610     getRepairXY : function(e, data){
18611         return data.node.ui.getDDRepairXY();
18612     },
18613     
18614     onEndDrag : function(data, e){
18615         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18616         
18617         
18618     },
18619     
18620     onValidDrop : function(dd, e, id){
18621         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18622         this.hideProxy();
18623     },
18624     
18625     beforeInvalidDrop : function(e, id){
18626         // this scrolls the original position back into view
18627         var sm = this.tree.getSelectionModel();
18628         sm.clearSelections();
18629         sm.select(this.dragData.node);
18630     }
18631 });
18632 }/*
18633  * Based on:
18634  * Ext JS Library 1.1.1
18635  * Copyright(c) 2006-2007, Ext JS, LLC.
18636  *
18637  * Originally Released Under LGPL - original licence link has changed is not relivant.
18638  *
18639  * Fork - LGPL
18640  * <script type="text/javascript">
18641  */
18642 /**
18643  * @class Roo.tree.TreeEditor
18644  * @extends Roo.Editor
18645  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18646  * as the editor field.
18647  * @constructor
18648  * @param {Object} config (used to be the tree panel.)
18649  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18650  * 
18651  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18652  * @cfg {Roo.form.TextField|Object} field The field configuration
18653  *
18654  * 
18655  */
18656 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18657     var tree = config;
18658     var field;
18659     if (oldconfig) { // old style..
18660         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18661     } else {
18662         // new style..
18663         tree = config.tree;
18664         config.field = config.field  || {};
18665         config.field.xtype = 'TextField';
18666         field = Roo.factory(config.field, Roo.form);
18667     }
18668     config = config || {};
18669     
18670     
18671     this.addEvents({
18672         /**
18673          * @event beforenodeedit
18674          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18675          * false from the handler of this event.
18676          * @param {Editor} this
18677          * @param {Roo.tree.Node} node 
18678          */
18679         "beforenodeedit" : true
18680     });
18681     
18682     //Roo.log(config);
18683     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18684
18685     this.tree = tree;
18686
18687     tree.on('beforeclick', this.beforeNodeClick, this);
18688     tree.getTreeEl().on('mousedown', this.hide, this);
18689     this.on('complete', this.updateNode, this);
18690     this.on('beforestartedit', this.fitToTree, this);
18691     this.on('startedit', this.bindScroll, this, {delay:10});
18692     this.on('specialkey', this.onSpecialKey, this);
18693 };
18694
18695 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18696     /**
18697      * @cfg {String} alignment
18698      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18699      */
18700     alignment: "l-l",
18701     // inherit
18702     autoSize: false,
18703     /**
18704      * @cfg {Boolean} hideEl
18705      * True to hide the bound element while the editor is displayed (defaults to false)
18706      */
18707     hideEl : false,
18708     /**
18709      * @cfg {String} cls
18710      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18711      */
18712     cls: "x-small-editor x-tree-editor",
18713     /**
18714      * @cfg {Boolean} shim
18715      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18716      */
18717     shim:false,
18718     // inherit
18719     shadow:"frame",
18720     /**
18721      * @cfg {Number} maxWidth
18722      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18723      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18724      * scroll and client offsets into account prior to each edit.
18725      */
18726     maxWidth: 250,
18727
18728     editDelay : 350,
18729
18730     // private
18731     fitToTree : function(ed, el){
18732         var td = this.tree.getTreeEl().dom, nd = el.dom;
18733         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18734             td.scrollLeft = nd.offsetLeft;
18735         }
18736         var w = Math.min(
18737                 this.maxWidth,
18738                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18739         this.setSize(w, '');
18740         
18741         return this.fireEvent('beforenodeedit', this, this.editNode);
18742         
18743     },
18744
18745     // private
18746     triggerEdit : function(node){
18747         this.completeEdit();
18748         this.editNode = node;
18749         this.startEdit(node.ui.textNode, node.text);
18750     },
18751
18752     // private
18753     bindScroll : function(){
18754         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18755     },
18756
18757     // private
18758     beforeNodeClick : function(node, e){
18759         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18760         this.lastClick = new Date();
18761         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18762             e.stopEvent();
18763             this.triggerEdit(node);
18764             return false;
18765         }
18766         return true;
18767     },
18768
18769     // private
18770     updateNode : function(ed, value){
18771         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18772         this.editNode.setText(value);
18773     },
18774
18775     // private
18776     onHide : function(){
18777         Roo.tree.TreeEditor.superclass.onHide.call(this);
18778         if(this.editNode){
18779             this.editNode.ui.focus();
18780         }
18781     },
18782
18783     // private
18784     onSpecialKey : function(field, e){
18785         var k = e.getKey();
18786         if(k == e.ESC){
18787             e.stopEvent();
18788             this.cancelEdit();
18789         }else if(k == e.ENTER && !e.hasModifier()){
18790             e.stopEvent();
18791             this.completeEdit();
18792         }
18793     }
18794 });//<Script type="text/javascript">
18795 /*
18796  * Based on:
18797  * Ext JS Library 1.1.1
18798  * Copyright(c) 2006-2007, Ext JS, LLC.
18799  *
18800  * Originally Released Under LGPL - original licence link has changed is not relivant.
18801  *
18802  * Fork - LGPL
18803  * <script type="text/javascript">
18804  */
18805  
18806 /**
18807  * Not documented??? - probably should be...
18808  */
18809
18810 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18811     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18812     
18813     renderElements : function(n, a, targetNode, bulkRender){
18814         //consel.log("renderElements?");
18815         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18816
18817         var t = n.getOwnerTree();
18818         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18819         
18820         var cols = t.columns;
18821         var bw = t.borderWidth;
18822         var c = cols[0];
18823         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18824          var cb = typeof a.checked == "boolean";
18825         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18826         var colcls = 'x-t-' + tid + '-c0';
18827         var buf = [
18828             '<li class="x-tree-node">',
18829             
18830                 
18831                 '<div class="x-tree-node-el ', a.cls,'">',
18832                     // extran...
18833                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18834                 
18835                 
18836                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18837                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18838                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18839                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18840                            (a.iconCls ? ' '+a.iconCls : ''),
18841                            '" unselectable="on" />',
18842                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18843                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18844                              
18845                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18846                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18847                             '<span unselectable="on" qtip="' + tx + '">',
18848                              tx,
18849                              '</span></a>' ,
18850                     '</div>',
18851                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18852                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18853                  ];
18854         for(var i = 1, len = cols.length; i < len; i++){
18855             c = cols[i];
18856             colcls = 'x-t-' + tid + '-c' +i;
18857             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18858             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18859                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18860                       "</div>");
18861          }
18862          
18863          buf.push(
18864             '</a>',
18865             '<div class="x-clear"></div></div>',
18866             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18867             "</li>");
18868         
18869         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18870             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18871                                 n.nextSibling.ui.getEl(), buf.join(""));
18872         }else{
18873             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18874         }
18875         var el = this.wrap.firstChild;
18876         this.elRow = el;
18877         this.elNode = el.firstChild;
18878         this.ranchor = el.childNodes[1];
18879         this.ctNode = this.wrap.childNodes[1];
18880         var cs = el.firstChild.childNodes;
18881         this.indentNode = cs[0];
18882         this.ecNode = cs[1];
18883         this.iconNode = cs[2];
18884         var index = 3;
18885         if(cb){
18886             this.checkbox = cs[3];
18887             index++;
18888         }
18889         this.anchor = cs[index];
18890         
18891         this.textNode = cs[index].firstChild;
18892         
18893         //el.on("click", this.onClick, this);
18894         //el.on("dblclick", this.onDblClick, this);
18895         
18896         
18897        // console.log(this);
18898     },
18899     initEvents : function(){
18900         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18901         
18902             
18903         var a = this.ranchor;
18904
18905         var el = Roo.get(a);
18906
18907         if(Roo.isOpera){ // opera render bug ignores the CSS
18908             el.setStyle("text-decoration", "none");
18909         }
18910
18911         el.on("click", this.onClick, this);
18912         el.on("dblclick", this.onDblClick, this);
18913         el.on("contextmenu", this.onContextMenu, this);
18914         
18915     },
18916     
18917     /*onSelectedChange : function(state){
18918         if(state){
18919             this.focus();
18920             this.addClass("x-tree-selected");
18921         }else{
18922             //this.blur();
18923             this.removeClass("x-tree-selected");
18924         }
18925     },*/
18926     addClass : function(cls){
18927         if(this.elRow){
18928             Roo.fly(this.elRow).addClass(cls);
18929         }
18930         
18931     },
18932     
18933     
18934     removeClass : function(cls){
18935         if(this.elRow){
18936             Roo.fly(this.elRow).removeClass(cls);
18937         }
18938     }
18939
18940     
18941     
18942 });//<Script type="text/javascript">
18943
18944 /*
18945  * Based on:
18946  * Ext JS Library 1.1.1
18947  * Copyright(c) 2006-2007, Ext JS, LLC.
18948  *
18949  * Originally Released Under LGPL - original licence link has changed is not relivant.
18950  *
18951  * Fork - LGPL
18952  * <script type="text/javascript">
18953  */
18954  
18955
18956 /**
18957  * @class Roo.tree.ColumnTree
18958  * @extends Roo.data.TreePanel
18959  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
18960  * @cfg {int} borderWidth  compined right/left border allowance
18961  * @constructor
18962  * @param {String/HTMLElement/Element} el The container element
18963  * @param {Object} config
18964  */
18965 Roo.tree.ColumnTree =  function(el, config)
18966 {
18967    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
18968    this.addEvents({
18969         /**
18970         * @event resize
18971         * Fire this event on a container when it resizes
18972         * @param {int} w Width
18973         * @param {int} h Height
18974         */
18975        "resize" : true
18976     });
18977     this.on('resize', this.onResize, this);
18978 };
18979
18980 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
18981     //lines:false,
18982     
18983     
18984     borderWidth: Roo.isBorderBox ? 0 : 2, 
18985     headEls : false,
18986     
18987     render : function(){
18988         // add the header.....
18989        
18990         Roo.tree.ColumnTree.superclass.render.apply(this);
18991         
18992         this.el.addClass('x-column-tree');
18993         
18994         this.headers = this.el.createChild(
18995             {cls:'x-tree-headers'},this.innerCt.dom);
18996    
18997         var cols = this.columns, c;
18998         var totalWidth = 0;
18999         this.headEls = [];
19000         var  len = cols.length;
19001         for(var i = 0; i < len; i++){
19002              c = cols[i];
19003              totalWidth += c.width;
19004             this.headEls.push(this.headers.createChild({
19005                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19006                  cn: {
19007                      cls:'x-tree-hd-text',
19008                      html: c.header
19009                  },
19010                  style:'width:'+(c.width-this.borderWidth)+'px;'
19011              }));
19012         }
19013         this.headers.createChild({cls:'x-clear'});
19014         // prevent floats from wrapping when clipped
19015         this.headers.setWidth(totalWidth);
19016         //this.innerCt.setWidth(totalWidth);
19017         this.innerCt.setStyle({ overflow: 'auto' });
19018         this.onResize(this.width, this.height);
19019              
19020         
19021     },
19022     onResize : function(w,h)
19023     {
19024         this.height = h;
19025         this.width = w;
19026         // resize cols..
19027         this.innerCt.setWidth(this.width);
19028         this.innerCt.setHeight(this.height-20);
19029         
19030         // headers...
19031         var cols = this.columns, c;
19032         var totalWidth = 0;
19033         var expEl = false;
19034         var len = cols.length;
19035         for(var i = 0; i < len; i++){
19036             c = cols[i];
19037             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19038                 // it's the expander..
19039                 expEl  = this.headEls[i];
19040                 continue;
19041             }
19042             totalWidth += c.width;
19043             
19044         }
19045         if (expEl) {
19046             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19047         }
19048         this.headers.setWidth(w-20);
19049
19050         
19051         
19052         
19053     }
19054 });
19055 /*
19056  * Based on:
19057  * Ext JS Library 1.1.1
19058  * Copyright(c) 2006-2007, Ext JS, LLC.
19059  *
19060  * Originally Released Under LGPL - original licence link has changed is not relivant.
19061  *
19062  * Fork - LGPL
19063  * <script type="text/javascript">
19064  */
19065  
19066 /**
19067  * @class Roo.menu.Menu
19068  * @extends Roo.util.Observable
19069  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19070  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19071  * @constructor
19072  * Creates a new Menu
19073  * @param {Object} config Configuration options
19074  */
19075 Roo.menu.Menu = function(config){
19076     Roo.apply(this, config);
19077     this.id = this.id || Roo.id();
19078     this.addEvents({
19079         /**
19080          * @event beforeshow
19081          * Fires before this menu is displayed
19082          * @param {Roo.menu.Menu} this
19083          */
19084         beforeshow : true,
19085         /**
19086          * @event beforehide
19087          * Fires before this menu is hidden
19088          * @param {Roo.menu.Menu} this
19089          */
19090         beforehide : true,
19091         /**
19092          * @event show
19093          * Fires after this menu is displayed
19094          * @param {Roo.menu.Menu} this
19095          */
19096         show : true,
19097         /**
19098          * @event hide
19099          * Fires after this menu is hidden
19100          * @param {Roo.menu.Menu} this
19101          */
19102         hide : true,
19103         /**
19104          * @event click
19105          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19106          * @param {Roo.menu.Menu} this
19107          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19108          * @param {Roo.EventObject} e
19109          */
19110         click : true,
19111         /**
19112          * @event mouseover
19113          * Fires when the mouse is hovering over this menu
19114          * @param {Roo.menu.Menu} this
19115          * @param {Roo.EventObject} e
19116          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19117          */
19118         mouseover : true,
19119         /**
19120          * @event mouseout
19121          * Fires when the mouse exits this menu
19122          * @param {Roo.menu.Menu} this
19123          * @param {Roo.EventObject} e
19124          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19125          */
19126         mouseout : true,
19127         /**
19128          * @event itemclick
19129          * Fires when a menu item contained in this menu is clicked
19130          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19131          * @param {Roo.EventObject} e
19132          */
19133         itemclick: true
19134     });
19135     if (this.registerMenu) {
19136         Roo.menu.MenuMgr.register(this);
19137     }
19138     
19139     var mis = this.items;
19140     this.items = new Roo.util.MixedCollection();
19141     if(mis){
19142         this.add.apply(this, mis);
19143     }
19144 };
19145
19146 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19147     /**
19148      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19149      */
19150     minWidth : 120,
19151     /**
19152      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19153      * for bottom-right shadow (defaults to "sides")
19154      */
19155     shadow : "sides",
19156     /**
19157      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19158      * this menu (defaults to "tl-tr?")
19159      */
19160     subMenuAlign : "tl-tr?",
19161     /**
19162      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19163      * relative to its element of origin (defaults to "tl-bl?")
19164      */
19165     defaultAlign : "tl-bl?",
19166     /**
19167      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19168      */
19169     allowOtherMenus : false,
19170     /**
19171      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19172      */
19173     registerMenu : true,
19174
19175     hidden:true,
19176
19177     // private
19178     render : function(){
19179         if(this.el){
19180             return;
19181         }
19182         var el = this.el = new Roo.Layer({
19183             cls: "x-menu",
19184             shadow:this.shadow,
19185             constrain: false,
19186             parentEl: this.parentEl || document.body,
19187             zindex:15000
19188         });
19189
19190         this.keyNav = new Roo.menu.MenuNav(this);
19191
19192         if(this.plain){
19193             el.addClass("x-menu-plain");
19194         }
19195         if(this.cls){
19196             el.addClass(this.cls);
19197         }
19198         // generic focus element
19199         this.focusEl = el.createChild({
19200             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19201         });
19202         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19203         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19204         
19205         ul.on("mouseover", this.onMouseOver, this);
19206         ul.on("mouseout", this.onMouseOut, this);
19207         this.items.each(function(item){
19208             if (item.hidden) {
19209                 return;
19210             }
19211             
19212             var li = document.createElement("li");
19213             li.className = "x-menu-list-item";
19214             ul.dom.appendChild(li);
19215             item.render(li, this);
19216         }, this);
19217         this.ul = ul;
19218         this.autoWidth();
19219     },
19220
19221     // private
19222     autoWidth : function(){
19223         var el = this.el, ul = this.ul;
19224         if(!el){
19225             return;
19226         }
19227         var w = this.width;
19228         if(w){
19229             el.setWidth(w);
19230         }else if(Roo.isIE){
19231             el.setWidth(this.minWidth);
19232             var t = el.dom.offsetWidth; // force recalc
19233             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19234         }
19235     },
19236
19237     // private
19238     delayAutoWidth : function(){
19239         if(this.rendered){
19240             if(!this.awTask){
19241                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19242             }
19243             this.awTask.delay(20);
19244         }
19245     },
19246
19247     // private
19248     findTargetItem : function(e){
19249         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19250         if(t && t.menuItemId){
19251             return this.items.get(t.menuItemId);
19252         }
19253     },
19254
19255     // private
19256     onClick : function(e){
19257         Roo.log("menu.onClick");
19258         var t = this.findTargetItem(e);
19259         if(!t){
19260             return;
19261         }
19262         Roo.log(e);
19263         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19264             if(t == this.activeItem && t.shouldDeactivate(e)){
19265                 this.activeItem.deactivate();
19266                 delete this.activeItem;
19267                 return;
19268             }
19269             if(t.canActivate){
19270                 this.setActiveItem(t, true);
19271             }
19272             return;
19273             
19274             
19275         }
19276         
19277         t.onClick(e);
19278         this.fireEvent("click", this, t, e);
19279     },
19280
19281     // private
19282     setActiveItem : function(item, autoExpand){
19283         if(item != this.activeItem){
19284             if(this.activeItem){
19285                 this.activeItem.deactivate();
19286             }
19287             this.activeItem = item;
19288             item.activate(autoExpand);
19289         }else if(autoExpand){
19290             item.expandMenu();
19291         }
19292     },
19293
19294     // private
19295     tryActivate : function(start, step){
19296         var items = this.items;
19297         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19298             var item = items.get(i);
19299             if(!item.disabled && item.canActivate){
19300                 this.setActiveItem(item, false);
19301                 return item;
19302             }
19303         }
19304         return false;
19305     },
19306
19307     // private
19308     onMouseOver : function(e){
19309         var t;
19310         if(t = this.findTargetItem(e)){
19311             if(t.canActivate && !t.disabled){
19312                 this.setActiveItem(t, true);
19313             }
19314         }
19315         this.fireEvent("mouseover", this, e, t);
19316     },
19317
19318     // private
19319     onMouseOut : function(e){
19320         var t;
19321         if(t = this.findTargetItem(e)){
19322             if(t == this.activeItem && t.shouldDeactivate(e)){
19323                 this.activeItem.deactivate();
19324                 delete this.activeItem;
19325             }
19326         }
19327         this.fireEvent("mouseout", this, e, t);
19328     },
19329
19330     /**
19331      * Read-only.  Returns true if the menu is currently displayed, else false.
19332      * @type Boolean
19333      */
19334     isVisible : function(){
19335         return this.el && !this.hidden;
19336     },
19337
19338     /**
19339      * Displays this menu relative to another element
19340      * @param {String/HTMLElement/Roo.Element} element The element to align to
19341      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19342      * the element (defaults to this.defaultAlign)
19343      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19344      */
19345     show : function(el, pos, parentMenu){
19346         this.parentMenu = parentMenu;
19347         if(!this.el){
19348             this.render();
19349         }
19350         this.fireEvent("beforeshow", this);
19351         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19352     },
19353
19354     /**
19355      * Displays this menu at a specific xy position
19356      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19357      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19358      */
19359     showAt : function(xy, parentMenu, /* private: */_e){
19360         this.parentMenu = parentMenu;
19361         if(!this.el){
19362             this.render();
19363         }
19364         if(_e !== false){
19365             this.fireEvent("beforeshow", this);
19366             xy = this.el.adjustForConstraints(xy);
19367         }
19368         this.el.setXY(xy);
19369         this.el.show();
19370         this.hidden = false;
19371         this.focus();
19372         this.fireEvent("show", this);
19373     },
19374
19375     focus : function(){
19376         if(!this.hidden){
19377             this.doFocus.defer(50, this);
19378         }
19379     },
19380
19381     doFocus : function(){
19382         if(!this.hidden){
19383             this.focusEl.focus();
19384         }
19385     },
19386
19387     /**
19388      * Hides this menu and optionally all parent menus
19389      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19390      */
19391     hide : function(deep){
19392         if(this.el && this.isVisible()){
19393             this.fireEvent("beforehide", this);
19394             if(this.activeItem){
19395                 this.activeItem.deactivate();
19396                 this.activeItem = null;
19397             }
19398             this.el.hide();
19399             this.hidden = true;
19400             this.fireEvent("hide", this);
19401         }
19402         if(deep === true && this.parentMenu){
19403             this.parentMenu.hide(true);
19404         }
19405     },
19406
19407     /**
19408      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19409      * Any of the following are valid:
19410      * <ul>
19411      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19412      * <li>An HTMLElement object which will be converted to a menu item</li>
19413      * <li>A menu item config object that will be created as a new menu item</li>
19414      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19415      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19416      * </ul>
19417      * Usage:
19418      * <pre><code>
19419 // Create the menu
19420 var menu = new Roo.menu.Menu();
19421
19422 // Create a menu item to add by reference
19423 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19424
19425 // Add a bunch of items at once using different methods.
19426 // Only the last item added will be returned.
19427 var item = menu.add(
19428     menuItem,                // add existing item by ref
19429     'Dynamic Item',          // new TextItem
19430     '-',                     // new separator
19431     { text: 'Config Item' }  // new item by config
19432 );
19433 </code></pre>
19434      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19435      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19436      */
19437     add : function(){
19438         var a = arguments, l = a.length, item;
19439         for(var i = 0; i < l; i++){
19440             var el = a[i];
19441             if ((typeof(el) == "object") && el.xtype && el.xns) {
19442                 el = Roo.factory(el, Roo.menu);
19443             }
19444             
19445             if(el.render){ // some kind of Item
19446                 item = this.addItem(el);
19447             }else if(typeof el == "string"){ // string
19448                 if(el == "separator" || el == "-"){
19449                     item = this.addSeparator();
19450                 }else{
19451                     item = this.addText(el);
19452                 }
19453             }else if(el.tagName || el.el){ // element
19454                 item = this.addElement(el);
19455             }else if(typeof el == "object"){ // must be menu item config?
19456                 item = this.addMenuItem(el);
19457             }
19458         }
19459         return item;
19460     },
19461
19462     /**
19463      * Returns this menu's underlying {@link Roo.Element} object
19464      * @return {Roo.Element} The element
19465      */
19466     getEl : function(){
19467         if(!this.el){
19468             this.render();
19469         }
19470         return this.el;
19471     },
19472
19473     /**
19474      * Adds a separator bar to the menu
19475      * @return {Roo.menu.Item} The menu item that was added
19476      */
19477     addSeparator : function(){
19478         return this.addItem(new Roo.menu.Separator());
19479     },
19480
19481     /**
19482      * Adds an {@link Roo.Element} object to the menu
19483      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19484      * @return {Roo.menu.Item} The menu item that was added
19485      */
19486     addElement : function(el){
19487         return this.addItem(new Roo.menu.BaseItem(el));
19488     },
19489
19490     /**
19491      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19492      * @param {Roo.menu.Item} item The menu item to add
19493      * @return {Roo.menu.Item} The menu item that was added
19494      */
19495     addItem : function(item){
19496         this.items.add(item);
19497         if(this.ul){
19498             var li = document.createElement("li");
19499             li.className = "x-menu-list-item";
19500             this.ul.dom.appendChild(li);
19501             item.render(li, this);
19502             this.delayAutoWidth();
19503         }
19504         return item;
19505     },
19506
19507     /**
19508      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19509      * @param {Object} config A MenuItem config object
19510      * @return {Roo.menu.Item} The menu item that was added
19511      */
19512     addMenuItem : function(config){
19513         if(!(config instanceof Roo.menu.Item)){
19514             if(typeof config.checked == "boolean"){ // must be check menu item config?
19515                 config = new Roo.menu.CheckItem(config);
19516             }else{
19517                 config = new Roo.menu.Item(config);
19518             }
19519         }
19520         return this.addItem(config);
19521     },
19522
19523     /**
19524      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19525      * @param {String} text The text to display in the menu item
19526      * @return {Roo.menu.Item} The menu item that was added
19527      */
19528     addText : function(text){
19529         return this.addItem(new Roo.menu.TextItem({ text : text }));
19530     },
19531
19532     /**
19533      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19534      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19535      * @param {Roo.menu.Item} item The menu item to add
19536      * @return {Roo.menu.Item} The menu item that was added
19537      */
19538     insert : function(index, item){
19539         this.items.insert(index, item);
19540         if(this.ul){
19541             var li = document.createElement("li");
19542             li.className = "x-menu-list-item";
19543             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19544             item.render(li, this);
19545             this.delayAutoWidth();
19546         }
19547         return item;
19548     },
19549
19550     /**
19551      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19552      * @param {Roo.menu.Item} item The menu item to remove
19553      */
19554     remove : function(item){
19555         this.items.removeKey(item.id);
19556         item.destroy();
19557     },
19558
19559     /**
19560      * Removes and destroys all items in the menu
19561      */
19562     removeAll : function(){
19563         var f;
19564         while(f = this.items.first()){
19565             this.remove(f);
19566         }
19567     }
19568 });
19569
19570 // MenuNav is a private utility class used internally by the Menu
19571 Roo.menu.MenuNav = function(menu){
19572     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19573     this.scope = this.menu = menu;
19574 };
19575
19576 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19577     doRelay : function(e, h){
19578         var k = e.getKey();
19579         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19580             this.menu.tryActivate(0, 1);
19581             return false;
19582         }
19583         return h.call(this.scope || this, e, this.menu);
19584     },
19585
19586     up : function(e, m){
19587         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19588             m.tryActivate(m.items.length-1, -1);
19589         }
19590     },
19591
19592     down : function(e, m){
19593         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19594             m.tryActivate(0, 1);
19595         }
19596     },
19597
19598     right : function(e, m){
19599         if(m.activeItem){
19600             m.activeItem.expandMenu(true);
19601         }
19602     },
19603
19604     left : function(e, m){
19605         m.hide();
19606         if(m.parentMenu && m.parentMenu.activeItem){
19607             m.parentMenu.activeItem.activate();
19608         }
19609     },
19610
19611     enter : function(e, m){
19612         if(m.activeItem){
19613             e.stopPropagation();
19614             m.activeItem.onClick(e);
19615             m.fireEvent("click", this, m.activeItem);
19616             return true;
19617         }
19618     }
19619 });/*
19620  * Based on:
19621  * Ext JS Library 1.1.1
19622  * Copyright(c) 2006-2007, Ext JS, LLC.
19623  *
19624  * Originally Released Under LGPL - original licence link has changed is not relivant.
19625  *
19626  * Fork - LGPL
19627  * <script type="text/javascript">
19628  */
19629  
19630 /**
19631  * @class Roo.menu.MenuMgr
19632  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19633  * @singleton
19634  */
19635 Roo.menu.MenuMgr = function(){
19636    var menus, active, groups = {}, attached = false, lastShow = new Date();
19637
19638    // private - called when first menu is created
19639    function init(){
19640        menus = {};
19641        active = new Roo.util.MixedCollection();
19642        Roo.get(document).addKeyListener(27, function(){
19643            if(active.length > 0){
19644                hideAll();
19645            }
19646        });
19647    }
19648
19649    // private
19650    function hideAll(){
19651        if(active && active.length > 0){
19652            var c = active.clone();
19653            c.each(function(m){
19654                m.hide();
19655            });
19656        }
19657    }
19658
19659    // private
19660    function onHide(m){
19661        active.remove(m);
19662        if(active.length < 1){
19663            Roo.get(document).un("mousedown", onMouseDown);
19664            attached = false;
19665        }
19666    }
19667
19668    // private
19669    function onShow(m){
19670        var last = active.last();
19671        lastShow = new Date();
19672        active.add(m);
19673        if(!attached){
19674            Roo.get(document).on("mousedown", onMouseDown);
19675            attached = true;
19676        }
19677        if(m.parentMenu){
19678           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19679           m.parentMenu.activeChild = m;
19680        }else if(last && last.isVisible()){
19681           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19682        }
19683    }
19684
19685    // private
19686    function onBeforeHide(m){
19687        if(m.activeChild){
19688            m.activeChild.hide();
19689        }
19690        if(m.autoHideTimer){
19691            clearTimeout(m.autoHideTimer);
19692            delete m.autoHideTimer;
19693        }
19694    }
19695
19696    // private
19697    function onBeforeShow(m){
19698        var pm = m.parentMenu;
19699        if(!pm && !m.allowOtherMenus){
19700            hideAll();
19701        }else if(pm && pm.activeChild && active != m){
19702            pm.activeChild.hide();
19703        }
19704    }
19705
19706    // private
19707    function onMouseDown(e){
19708        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19709            hideAll();
19710        }
19711    }
19712
19713    // private
19714    function onBeforeCheck(mi, state){
19715        if(state){
19716            var g = groups[mi.group];
19717            for(var i = 0, l = g.length; i < l; i++){
19718                if(g[i] != mi){
19719                    g[i].setChecked(false);
19720                }
19721            }
19722        }
19723    }
19724
19725    return {
19726
19727        /**
19728         * Hides all menus that are currently visible
19729         */
19730        hideAll : function(){
19731             hideAll();  
19732        },
19733
19734        // private
19735        register : function(menu){
19736            if(!menus){
19737                init();
19738            }
19739            menus[menu.id] = menu;
19740            menu.on("beforehide", onBeforeHide);
19741            menu.on("hide", onHide);
19742            menu.on("beforeshow", onBeforeShow);
19743            menu.on("show", onShow);
19744            var g = menu.group;
19745            if(g && menu.events["checkchange"]){
19746                if(!groups[g]){
19747                    groups[g] = [];
19748                }
19749                groups[g].push(menu);
19750                menu.on("checkchange", onCheck);
19751            }
19752        },
19753
19754         /**
19755          * Returns a {@link Roo.menu.Menu} object
19756          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19757          * be used to generate and return a new Menu instance.
19758          */
19759        get : function(menu){
19760            if(typeof menu == "string"){ // menu id
19761                return menus[menu];
19762            }else if(menu.events){  // menu instance
19763                return menu;
19764            }else if(typeof menu.length == 'number'){ // array of menu items?
19765                return new Roo.menu.Menu({items:menu});
19766            }else{ // otherwise, must be a config
19767                return new Roo.menu.Menu(menu);
19768            }
19769        },
19770
19771        // private
19772        unregister : function(menu){
19773            delete menus[menu.id];
19774            menu.un("beforehide", onBeforeHide);
19775            menu.un("hide", onHide);
19776            menu.un("beforeshow", onBeforeShow);
19777            menu.un("show", onShow);
19778            var g = menu.group;
19779            if(g && menu.events["checkchange"]){
19780                groups[g].remove(menu);
19781                menu.un("checkchange", onCheck);
19782            }
19783        },
19784
19785        // private
19786        registerCheckable : function(menuItem){
19787            var g = menuItem.group;
19788            if(g){
19789                if(!groups[g]){
19790                    groups[g] = [];
19791                }
19792                groups[g].push(menuItem);
19793                menuItem.on("beforecheckchange", onBeforeCheck);
19794            }
19795        },
19796
19797        // private
19798        unregisterCheckable : function(menuItem){
19799            var g = menuItem.group;
19800            if(g){
19801                groups[g].remove(menuItem);
19802                menuItem.un("beforecheckchange", onBeforeCheck);
19803            }
19804        }
19805    };
19806 }();/*
19807  * Based on:
19808  * Ext JS Library 1.1.1
19809  * Copyright(c) 2006-2007, Ext JS, LLC.
19810  *
19811  * Originally Released Under LGPL - original licence link has changed is not relivant.
19812  *
19813  * Fork - LGPL
19814  * <script type="text/javascript">
19815  */
19816  
19817
19818 /**
19819  * @class Roo.menu.BaseItem
19820  * @extends Roo.Component
19821  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19822  * management and base configuration options shared by all menu components.
19823  * @constructor
19824  * Creates a new BaseItem
19825  * @param {Object} config Configuration options
19826  */
19827 Roo.menu.BaseItem = function(config){
19828     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19829
19830     this.addEvents({
19831         /**
19832          * @event click
19833          * Fires when this item is clicked
19834          * @param {Roo.menu.BaseItem} this
19835          * @param {Roo.EventObject} e
19836          */
19837         click: true,
19838         /**
19839          * @event activate
19840          * Fires when this item is activated
19841          * @param {Roo.menu.BaseItem} this
19842          */
19843         activate : true,
19844         /**
19845          * @event deactivate
19846          * Fires when this item is deactivated
19847          * @param {Roo.menu.BaseItem} this
19848          */
19849         deactivate : true
19850     });
19851
19852     if(this.handler){
19853         this.on("click", this.handler, this.scope, true);
19854     }
19855 };
19856
19857 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19858     /**
19859      * @cfg {Function} handler
19860      * A function that will handle the click event of this menu item (defaults to undefined)
19861      */
19862     /**
19863      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19864      */
19865     canActivate : false,
19866     
19867      /**
19868      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19869      */
19870     hidden: false,
19871     
19872     /**
19873      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19874      */
19875     activeClass : "x-menu-item-active",
19876     /**
19877      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19878      */
19879     hideOnClick : true,
19880     /**
19881      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19882      */
19883     hideDelay : 100,
19884
19885     // private
19886     ctype: "Roo.menu.BaseItem",
19887
19888     // private
19889     actionMode : "container",
19890
19891     // private
19892     render : function(container, parentMenu){
19893         this.parentMenu = parentMenu;
19894         Roo.menu.BaseItem.superclass.render.call(this, container);
19895         this.container.menuItemId = this.id;
19896     },
19897
19898     // private
19899     onRender : function(container, position){
19900         this.el = Roo.get(this.el);
19901         container.dom.appendChild(this.el.dom);
19902     },
19903
19904     // private
19905     onClick : function(e){
19906         if(!this.disabled && this.fireEvent("click", this, e) !== false
19907                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19908             this.handleClick(e);
19909         }else{
19910             e.stopEvent();
19911         }
19912     },
19913
19914     // private
19915     activate : function(){
19916         if(this.disabled){
19917             return false;
19918         }
19919         var li = this.container;
19920         li.addClass(this.activeClass);
19921         this.region = li.getRegion().adjust(2, 2, -2, -2);
19922         this.fireEvent("activate", this);
19923         return true;
19924     },
19925
19926     // private
19927     deactivate : function(){
19928         this.container.removeClass(this.activeClass);
19929         this.fireEvent("deactivate", this);
19930     },
19931
19932     // private
19933     shouldDeactivate : function(e){
19934         return !this.region || !this.region.contains(e.getPoint());
19935     },
19936
19937     // private
19938     handleClick : function(e){
19939         if(this.hideOnClick){
19940             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19941         }
19942     },
19943
19944     // private
19945     expandMenu : function(autoActivate){
19946         // do nothing
19947     },
19948
19949     // private
19950     hideMenu : function(){
19951         // do nothing
19952     }
19953 });/*
19954  * Based on:
19955  * Ext JS Library 1.1.1
19956  * Copyright(c) 2006-2007, Ext JS, LLC.
19957  *
19958  * Originally Released Under LGPL - original licence link has changed is not relivant.
19959  *
19960  * Fork - LGPL
19961  * <script type="text/javascript">
19962  */
19963  
19964 /**
19965  * @class Roo.menu.Adapter
19966  * @extends Roo.menu.BaseItem
19967  * 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.
19968  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
19969  * @constructor
19970  * Creates a new Adapter
19971  * @param {Object} config Configuration options
19972  */
19973 Roo.menu.Adapter = function(component, config){
19974     Roo.menu.Adapter.superclass.constructor.call(this, config);
19975     this.component = component;
19976 };
19977 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
19978     // private
19979     canActivate : true,
19980
19981     // private
19982     onRender : function(container, position){
19983         this.component.render(container);
19984         this.el = this.component.getEl();
19985     },
19986
19987     // private
19988     activate : function(){
19989         if(this.disabled){
19990             return false;
19991         }
19992         this.component.focus();
19993         this.fireEvent("activate", this);
19994         return true;
19995     },
19996
19997     // private
19998     deactivate : function(){
19999         this.fireEvent("deactivate", this);
20000     },
20001
20002     // private
20003     disable : function(){
20004         this.component.disable();
20005         Roo.menu.Adapter.superclass.disable.call(this);
20006     },
20007
20008     // private
20009     enable : function(){
20010         this.component.enable();
20011         Roo.menu.Adapter.superclass.enable.call(this);
20012     }
20013 });/*
20014  * Based on:
20015  * Ext JS Library 1.1.1
20016  * Copyright(c) 2006-2007, Ext JS, LLC.
20017  *
20018  * Originally Released Under LGPL - original licence link has changed is not relivant.
20019  *
20020  * Fork - LGPL
20021  * <script type="text/javascript">
20022  */
20023
20024 /**
20025  * @class Roo.menu.TextItem
20026  * @extends Roo.menu.BaseItem
20027  * Adds a static text string to a menu, usually used as either a heading or group separator.
20028  * Note: old style constructor with text is still supported.
20029  * 
20030  * @constructor
20031  * Creates a new TextItem
20032  * @param {Object} cfg Configuration
20033  */
20034 Roo.menu.TextItem = function(cfg){
20035     if (typeof(cfg) == 'string') {
20036         this.text = cfg;
20037     } else {
20038         Roo.apply(this,cfg);
20039     }
20040     
20041     Roo.menu.TextItem.superclass.constructor.call(this);
20042 };
20043
20044 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20045     /**
20046      * @cfg {Boolean} text Text to show on item.
20047      */
20048     text : '',
20049     
20050     /**
20051      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20052      */
20053     hideOnClick : false,
20054     /**
20055      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20056      */
20057     itemCls : "x-menu-text",
20058
20059     // private
20060     onRender : function(){
20061         var s = document.createElement("span");
20062         s.className = this.itemCls;
20063         s.innerHTML = this.text;
20064         this.el = s;
20065         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20066     }
20067 });/*
20068  * Based on:
20069  * Ext JS Library 1.1.1
20070  * Copyright(c) 2006-2007, Ext JS, LLC.
20071  *
20072  * Originally Released Under LGPL - original licence link has changed is not relivant.
20073  *
20074  * Fork - LGPL
20075  * <script type="text/javascript">
20076  */
20077
20078 /**
20079  * @class Roo.menu.Separator
20080  * @extends Roo.menu.BaseItem
20081  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20082  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20083  * @constructor
20084  * @param {Object} config Configuration options
20085  */
20086 Roo.menu.Separator = function(config){
20087     Roo.menu.Separator.superclass.constructor.call(this, config);
20088 };
20089
20090 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20091     /**
20092      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20093      */
20094     itemCls : "x-menu-sep",
20095     /**
20096      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20097      */
20098     hideOnClick : false,
20099
20100     // private
20101     onRender : function(li){
20102         var s = document.createElement("span");
20103         s.className = this.itemCls;
20104         s.innerHTML = "&#160;";
20105         this.el = s;
20106         li.addClass("x-menu-sep-li");
20107         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20108     }
20109 });/*
20110  * Based on:
20111  * Ext JS Library 1.1.1
20112  * Copyright(c) 2006-2007, Ext JS, LLC.
20113  *
20114  * Originally Released Under LGPL - original licence link has changed is not relivant.
20115  *
20116  * Fork - LGPL
20117  * <script type="text/javascript">
20118  */
20119 /**
20120  * @class Roo.menu.Item
20121  * @extends Roo.menu.BaseItem
20122  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20123  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20124  * activation and click handling.
20125  * @constructor
20126  * Creates a new Item
20127  * @param {Object} config Configuration options
20128  */
20129 Roo.menu.Item = function(config){
20130     Roo.menu.Item.superclass.constructor.call(this, config);
20131     if(this.menu){
20132         this.menu = Roo.menu.MenuMgr.get(this.menu);
20133     }
20134 };
20135 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20136     
20137     /**
20138      * @cfg {String} text
20139      * The text to show on the menu item.
20140      */
20141     text: '',
20142      /**
20143      * @cfg {String} HTML to render in menu
20144      * The text to show on the menu item (HTML version).
20145      */
20146     html: '',
20147     /**
20148      * @cfg {String} icon
20149      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20150      */
20151     icon: undefined,
20152     /**
20153      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20154      */
20155     itemCls : "x-menu-item",
20156     /**
20157      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20158      */
20159     canActivate : true,
20160     /**
20161      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20162      */
20163     showDelay: 200,
20164     // doc'd in BaseItem
20165     hideDelay: 200,
20166
20167     // private
20168     ctype: "Roo.menu.Item",
20169     
20170     // private
20171     onRender : function(container, position){
20172         var el = document.createElement("a");
20173         el.hideFocus = true;
20174         el.unselectable = "on";
20175         el.href = this.href || "#";
20176         if(this.hrefTarget){
20177             el.target = this.hrefTarget;
20178         }
20179         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20180         
20181         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20182         
20183         el.innerHTML = String.format(
20184                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20185                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20186         this.el = el;
20187         Roo.menu.Item.superclass.onRender.call(this, container, position);
20188     },
20189
20190     /**
20191      * Sets the text to display in this menu item
20192      * @param {String} text The text to display
20193      * @param {Boolean} isHTML true to indicate text is pure html.
20194      */
20195     setText : function(text, isHTML){
20196         if (isHTML) {
20197             this.html = text;
20198         } else {
20199             this.text = text;
20200             this.html = '';
20201         }
20202         if(this.rendered){
20203             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20204      
20205             this.el.update(String.format(
20206                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20207                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20208             this.parentMenu.autoWidth();
20209         }
20210     },
20211
20212     // private
20213     handleClick : function(e){
20214         if(!this.href){ // if no link defined, stop the event automatically
20215             e.stopEvent();
20216         }
20217         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20218     },
20219
20220     // private
20221     activate : function(autoExpand){
20222         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20223             this.focus();
20224             if(autoExpand){
20225                 this.expandMenu();
20226             }
20227         }
20228         return true;
20229     },
20230
20231     // private
20232     shouldDeactivate : function(e){
20233         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20234             if(this.menu && this.menu.isVisible()){
20235                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20236             }
20237             return true;
20238         }
20239         return false;
20240     },
20241
20242     // private
20243     deactivate : function(){
20244         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20245         this.hideMenu();
20246     },
20247
20248     // private
20249     expandMenu : function(autoActivate){
20250         if(!this.disabled && this.menu){
20251             clearTimeout(this.hideTimer);
20252             delete this.hideTimer;
20253             if(!this.menu.isVisible() && !this.showTimer){
20254                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20255             }else if (this.menu.isVisible() && autoActivate){
20256                 this.menu.tryActivate(0, 1);
20257             }
20258         }
20259     },
20260
20261     // private
20262     deferExpand : function(autoActivate){
20263         delete this.showTimer;
20264         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20265         if(autoActivate){
20266             this.menu.tryActivate(0, 1);
20267         }
20268     },
20269
20270     // private
20271     hideMenu : function(){
20272         clearTimeout(this.showTimer);
20273         delete this.showTimer;
20274         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20275             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20276         }
20277     },
20278
20279     // private
20280     deferHide : function(){
20281         delete this.hideTimer;
20282         this.menu.hide();
20283     }
20284 });/*
20285  * Based on:
20286  * Ext JS Library 1.1.1
20287  * Copyright(c) 2006-2007, Ext JS, LLC.
20288  *
20289  * Originally Released Under LGPL - original licence link has changed is not relivant.
20290  *
20291  * Fork - LGPL
20292  * <script type="text/javascript">
20293  */
20294  
20295 /**
20296  * @class Roo.menu.CheckItem
20297  * @extends Roo.menu.Item
20298  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20299  * @constructor
20300  * Creates a new CheckItem
20301  * @param {Object} config Configuration options
20302  */
20303 Roo.menu.CheckItem = function(config){
20304     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20305     this.addEvents({
20306         /**
20307          * @event beforecheckchange
20308          * Fires before the checked value is set, providing an opportunity to cancel if needed
20309          * @param {Roo.menu.CheckItem} this
20310          * @param {Boolean} checked The new checked value that will be set
20311          */
20312         "beforecheckchange" : true,
20313         /**
20314          * @event checkchange
20315          * Fires after the checked value has been set
20316          * @param {Roo.menu.CheckItem} this
20317          * @param {Boolean} checked The checked value that was set
20318          */
20319         "checkchange" : true
20320     });
20321     if(this.checkHandler){
20322         this.on('checkchange', this.checkHandler, this.scope);
20323     }
20324 };
20325 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20326     /**
20327      * @cfg {String} group
20328      * All check items with the same group name will automatically be grouped into a single-select
20329      * radio button group (defaults to '')
20330      */
20331     /**
20332      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20333      */
20334     itemCls : "x-menu-item x-menu-check-item",
20335     /**
20336      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20337      */
20338     groupClass : "x-menu-group-item",
20339
20340     /**
20341      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20342      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20343      * initialized with checked = true will be rendered as checked.
20344      */
20345     checked: false,
20346
20347     // private
20348     ctype: "Roo.menu.CheckItem",
20349
20350     // private
20351     onRender : function(c){
20352         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20353         if(this.group){
20354             this.el.addClass(this.groupClass);
20355         }
20356         Roo.menu.MenuMgr.registerCheckable(this);
20357         if(this.checked){
20358             this.checked = false;
20359             this.setChecked(true, true);
20360         }
20361     },
20362
20363     // private
20364     destroy : function(){
20365         if(this.rendered){
20366             Roo.menu.MenuMgr.unregisterCheckable(this);
20367         }
20368         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20369     },
20370
20371     /**
20372      * Set the checked state of this item
20373      * @param {Boolean} checked The new checked value
20374      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20375      */
20376     setChecked : function(state, suppressEvent){
20377         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20378             if(this.container){
20379                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20380             }
20381             this.checked = state;
20382             if(suppressEvent !== true){
20383                 this.fireEvent("checkchange", this, state);
20384             }
20385         }
20386     },
20387
20388     // private
20389     handleClick : function(e){
20390        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20391            this.setChecked(!this.checked);
20392        }
20393        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20394     }
20395 });/*
20396  * Based on:
20397  * Ext JS Library 1.1.1
20398  * Copyright(c) 2006-2007, Ext JS, LLC.
20399  *
20400  * Originally Released Under LGPL - original licence link has changed is not relivant.
20401  *
20402  * Fork - LGPL
20403  * <script type="text/javascript">
20404  */
20405  
20406 /**
20407  * @class Roo.menu.DateItem
20408  * @extends Roo.menu.Adapter
20409  * A menu item that wraps the {@link Roo.DatPicker} component.
20410  * @constructor
20411  * Creates a new DateItem
20412  * @param {Object} config Configuration options
20413  */
20414 Roo.menu.DateItem = function(config){
20415     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20416     /** The Roo.DatePicker object @type Roo.DatePicker */
20417     this.picker = this.component;
20418     this.addEvents({select: true});
20419     
20420     this.picker.on("render", function(picker){
20421         picker.getEl().swallowEvent("click");
20422         picker.container.addClass("x-menu-date-item");
20423     });
20424
20425     this.picker.on("select", this.onSelect, this);
20426 };
20427
20428 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20429     // private
20430     onSelect : function(picker, date){
20431         this.fireEvent("select", this, date, picker);
20432         Roo.menu.DateItem.superclass.handleClick.call(this);
20433     }
20434 });/*
20435  * Based on:
20436  * Ext JS Library 1.1.1
20437  * Copyright(c) 2006-2007, Ext JS, LLC.
20438  *
20439  * Originally Released Under LGPL - original licence link has changed is not relivant.
20440  *
20441  * Fork - LGPL
20442  * <script type="text/javascript">
20443  */
20444  
20445 /**
20446  * @class Roo.menu.ColorItem
20447  * @extends Roo.menu.Adapter
20448  * A menu item that wraps the {@link Roo.ColorPalette} component.
20449  * @constructor
20450  * Creates a new ColorItem
20451  * @param {Object} config Configuration options
20452  */
20453 Roo.menu.ColorItem = function(config){
20454     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20455     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20456     this.palette = this.component;
20457     this.relayEvents(this.palette, ["select"]);
20458     if(this.selectHandler){
20459         this.on('select', this.selectHandler, this.scope);
20460     }
20461 };
20462 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20463  * Based on:
20464  * Ext JS Library 1.1.1
20465  * Copyright(c) 2006-2007, Ext JS, LLC.
20466  *
20467  * Originally Released Under LGPL - original licence link has changed is not relivant.
20468  *
20469  * Fork - LGPL
20470  * <script type="text/javascript">
20471  */
20472  
20473
20474 /**
20475  * @class Roo.menu.DateMenu
20476  * @extends Roo.menu.Menu
20477  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20478  * @constructor
20479  * Creates a new DateMenu
20480  * @param {Object} config Configuration options
20481  */
20482 Roo.menu.DateMenu = function(config){
20483     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20484     this.plain = true;
20485     var di = new Roo.menu.DateItem(config);
20486     this.add(di);
20487     /**
20488      * The {@link Roo.DatePicker} instance for this DateMenu
20489      * @type DatePicker
20490      */
20491     this.picker = di.picker;
20492     /**
20493      * @event select
20494      * @param {DatePicker} picker
20495      * @param {Date} date
20496      */
20497     this.relayEvents(di, ["select"]);
20498     this.on('beforeshow', function(){
20499         if(this.picker){
20500             this.picker.hideMonthPicker(false);
20501         }
20502     }, this);
20503 };
20504 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20505     cls:'x-date-menu'
20506 });/*
20507  * Based on:
20508  * Ext JS Library 1.1.1
20509  * Copyright(c) 2006-2007, Ext JS, LLC.
20510  *
20511  * Originally Released Under LGPL - original licence link has changed is not relivant.
20512  *
20513  * Fork - LGPL
20514  * <script type="text/javascript">
20515  */
20516  
20517
20518 /**
20519  * @class Roo.menu.ColorMenu
20520  * @extends Roo.menu.Menu
20521  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20522  * @constructor
20523  * Creates a new ColorMenu
20524  * @param {Object} config Configuration options
20525  */
20526 Roo.menu.ColorMenu = function(config){
20527     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20528     this.plain = true;
20529     var ci = new Roo.menu.ColorItem(config);
20530     this.add(ci);
20531     /**
20532      * The {@link Roo.ColorPalette} instance for this ColorMenu
20533      * @type ColorPalette
20534      */
20535     this.palette = ci.palette;
20536     /**
20537      * @event select
20538      * @param {ColorPalette} palette
20539      * @param {String} color
20540      */
20541     this.relayEvents(ci, ["select"]);
20542 };
20543 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20544  * Based on:
20545  * Ext JS Library 1.1.1
20546  * Copyright(c) 2006-2007, Ext JS, LLC.
20547  *
20548  * Originally Released Under LGPL - original licence link has changed is not relivant.
20549  *
20550  * Fork - LGPL
20551  * <script type="text/javascript">
20552  */
20553  
20554 /**
20555  * @class Roo.form.Field
20556  * @extends Roo.BoxComponent
20557  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20558  * @constructor
20559  * Creates a new Field
20560  * @param {Object} config Configuration options
20561  */
20562 Roo.form.Field = function(config){
20563     Roo.form.Field.superclass.constructor.call(this, config);
20564 };
20565
20566 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20567     /**
20568      * @cfg {String} fieldLabel Label to use when rendering a form.
20569      */
20570        /**
20571      * @cfg {String} qtip Mouse over tip
20572      */
20573      
20574     /**
20575      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20576      */
20577     invalidClass : "x-form-invalid",
20578     /**
20579      * @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")
20580      */
20581     invalidText : "The value in this field is invalid",
20582     /**
20583      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20584      */
20585     focusClass : "x-form-focus",
20586     /**
20587      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20588       automatic validation (defaults to "keyup").
20589      */
20590     validationEvent : "keyup",
20591     /**
20592      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20593      */
20594     validateOnBlur : true,
20595     /**
20596      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20597      */
20598     validationDelay : 250,
20599     /**
20600      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20601      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20602      */
20603     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20604     /**
20605      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20606      */
20607     fieldClass : "x-form-field",
20608     /**
20609      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20610      *<pre>
20611 Value         Description
20612 -----------   ----------------------------------------------------------------------
20613 qtip          Display a quick tip when the user hovers over the field
20614 title         Display a default browser title attribute popup
20615 under         Add a block div beneath the field containing the error text
20616 side          Add an error icon to the right of the field with a popup on hover
20617 [element id]  Add the error text directly to the innerHTML of the specified element
20618 </pre>
20619      */
20620     msgTarget : 'qtip',
20621     /**
20622      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20623      */
20624     msgFx : 'normal',
20625
20626     /**
20627      * @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.
20628      */
20629     readOnly : false,
20630
20631     /**
20632      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20633      */
20634     disabled : false,
20635
20636     /**
20637      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20638      */
20639     inputType : undefined,
20640     
20641     /**
20642      * @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).
20643          */
20644         tabIndex : undefined,
20645         
20646     // private
20647     isFormField : true,
20648
20649     // private
20650     hasFocus : false,
20651     /**
20652      * @property {Roo.Element} fieldEl
20653      * Element Containing the rendered Field (with label etc.)
20654      */
20655     /**
20656      * @cfg {Mixed} value A value to initialize this field with.
20657      */
20658     value : undefined,
20659
20660     /**
20661      * @cfg {String} name The field's HTML name attribute.
20662      */
20663     /**
20664      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20665      */
20666
20667         // private ??
20668         initComponent : function(){
20669         Roo.form.Field.superclass.initComponent.call(this);
20670         this.addEvents({
20671             /**
20672              * @event focus
20673              * Fires when this field receives input focus.
20674              * @param {Roo.form.Field} this
20675              */
20676             focus : true,
20677             /**
20678              * @event blur
20679              * Fires when this field loses input focus.
20680              * @param {Roo.form.Field} this
20681              */
20682             blur : true,
20683             /**
20684              * @event specialkey
20685              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20686              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20687              * @param {Roo.form.Field} this
20688              * @param {Roo.EventObject} e The event object
20689              */
20690             specialkey : true,
20691             /**
20692              * @event change
20693              * Fires just before the field blurs if the field value has changed.
20694              * @param {Roo.form.Field} this
20695              * @param {Mixed} newValue The new value
20696              * @param {Mixed} oldValue The original value
20697              */
20698             change : true,
20699             /**
20700              * @event invalid
20701              * Fires after the field has been marked as invalid.
20702              * @param {Roo.form.Field} this
20703              * @param {String} msg The validation message
20704              */
20705             invalid : true,
20706             /**
20707              * @event valid
20708              * Fires after the field has been validated with no errors.
20709              * @param {Roo.form.Field} this
20710              */
20711             valid : true,
20712              /**
20713              * @event keyup
20714              * Fires after the key up
20715              * @param {Roo.form.Field} this
20716              * @param {Roo.EventObject}  e The event Object
20717              */
20718             keyup : true
20719         });
20720     },
20721
20722     /**
20723      * Returns the name attribute of the field if available
20724      * @return {String} name The field name
20725      */
20726     getName: function(){
20727          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20728     },
20729
20730     // private
20731     onRender : function(ct, position){
20732         Roo.form.Field.superclass.onRender.call(this, ct, position);
20733         if(!this.el){
20734             var cfg = this.getAutoCreate();
20735             if(!cfg.name){
20736                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20737             }
20738             if (!cfg.name.length) {
20739                 delete cfg.name;
20740             }
20741             if(this.inputType){
20742                 cfg.type = this.inputType;
20743             }
20744             this.el = ct.createChild(cfg, position);
20745         }
20746         var type = this.el.dom.type;
20747         if(type){
20748             if(type == 'password'){
20749                 type = 'text';
20750             }
20751             this.el.addClass('x-form-'+type);
20752         }
20753         if(this.readOnly){
20754             this.el.dom.readOnly = true;
20755         }
20756         if(this.tabIndex !== undefined){
20757             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20758         }
20759
20760         this.el.addClass([this.fieldClass, this.cls]);
20761         this.initValue();
20762     },
20763
20764     /**
20765      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20766      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20767      * @return {Roo.form.Field} this
20768      */
20769     applyTo : function(target){
20770         this.allowDomMove = false;
20771         this.el = Roo.get(target);
20772         this.render(this.el.dom.parentNode);
20773         return this;
20774     },
20775
20776     // private
20777     initValue : function(){
20778         if(this.value !== undefined){
20779             this.setValue(this.value);
20780         }else if(this.el.dom.value.length > 0){
20781             this.setValue(this.el.dom.value);
20782         }
20783     },
20784
20785     /**
20786      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20787      */
20788     isDirty : function() {
20789         if(this.disabled) {
20790             return false;
20791         }
20792         return String(this.getValue()) !== String(this.originalValue);
20793     },
20794
20795     // private
20796     afterRender : function(){
20797         Roo.form.Field.superclass.afterRender.call(this);
20798         this.initEvents();
20799     },
20800
20801     // private
20802     fireKey : function(e){
20803         //Roo.log('field ' + e.getKey());
20804         if(e.isNavKeyPress()){
20805             this.fireEvent("specialkey", this, e);
20806         }
20807     },
20808
20809     /**
20810      * Resets the current field value to the originally loaded value and clears any validation messages
20811      */
20812     reset : function(){
20813         this.setValue(this.resetValue);
20814         this.clearInvalid();
20815     },
20816
20817     // private
20818     initEvents : function(){
20819         // safari killled keypress - so keydown is now used..
20820         this.el.on("keydown" , this.fireKey,  this);
20821         this.el.on("focus", this.onFocus,  this);
20822         this.el.on("blur", this.onBlur,  this);
20823         this.el.relayEvent('keyup', this);
20824
20825         // reference to original value for reset
20826         this.originalValue = this.getValue();
20827         this.resetValue =  this.getValue();
20828     },
20829
20830     // private
20831     onFocus : function(){
20832         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20833             this.el.addClass(this.focusClass);
20834         }
20835         if(!this.hasFocus){
20836             this.hasFocus = true;
20837             this.startValue = this.getValue();
20838             this.fireEvent("focus", this);
20839         }
20840     },
20841
20842     beforeBlur : Roo.emptyFn,
20843
20844     // private
20845     onBlur : function(){
20846         this.beforeBlur();
20847         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20848             this.el.removeClass(this.focusClass);
20849         }
20850         this.hasFocus = false;
20851         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20852             this.validate();
20853         }
20854         var v = this.getValue();
20855         if(String(v) !== String(this.startValue)){
20856             this.fireEvent('change', this, v, this.startValue);
20857         }
20858         this.fireEvent("blur", this);
20859     },
20860
20861     /**
20862      * Returns whether or not the field value is currently valid
20863      * @param {Boolean} preventMark True to disable marking the field invalid
20864      * @return {Boolean} True if the value is valid, else false
20865      */
20866     isValid : function(preventMark){
20867         if(this.disabled){
20868             return true;
20869         }
20870         var restore = this.preventMark;
20871         this.preventMark = preventMark === true;
20872         var v = this.validateValue(this.processValue(this.getRawValue()));
20873         this.preventMark = restore;
20874         return v;
20875     },
20876
20877     /**
20878      * Validates the field value
20879      * @return {Boolean} True if the value is valid, else false
20880      */
20881     validate : function(){
20882         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20883             this.clearInvalid();
20884             return true;
20885         }
20886         return false;
20887     },
20888
20889     processValue : function(value){
20890         return value;
20891     },
20892
20893     // private
20894     // Subclasses should provide the validation implementation by overriding this
20895     validateValue : function(value){
20896         return true;
20897     },
20898
20899     /**
20900      * Mark this field as invalid
20901      * @param {String} msg The validation message
20902      */
20903     markInvalid : function(msg){
20904         if(!this.rendered || this.preventMark){ // not rendered
20905             return;
20906         }
20907         
20908         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20909         
20910         obj.el.addClass(this.invalidClass);
20911         msg = msg || this.invalidText;
20912         switch(this.msgTarget){
20913             case 'qtip':
20914                 obj.el.dom.qtip = msg;
20915                 obj.el.dom.qclass = 'x-form-invalid-tip';
20916                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20917                     Roo.QuickTips.enable();
20918                 }
20919                 break;
20920             case 'title':
20921                 this.el.dom.title = msg;
20922                 break;
20923             case 'under':
20924                 if(!this.errorEl){
20925                     var elp = this.el.findParent('.x-form-element', 5, true);
20926                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20927                     this.errorEl.setWidth(elp.getWidth(true)-20);
20928                 }
20929                 this.errorEl.update(msg);
20930                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20931                 break;
20932             case 'side':
20933                 if(!this.errorIcon){
20934                     var elp = this.el.findParent('.x-form-element', 5, true);
20935                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20936                 }
20937                 this.alignErrorIcon();
20938                 this.errorIcon.dom.qtip = msg;
20939                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20940                 this.errorIcon.show();
20941                 this.on('resize', this.alignErrorIcon, this);
20942                 break;
20943             default:
20944                 var t = Roo.getDom(this.msgTarget);
20945                 t.innerHTML = msg;
20946                 t.style.display = this.msgDisplay;
20947                 break;
20948         }
20949         this.fireEvent('invalid', this, msg);
20950     },
20951
20952     // private
20953     alignErrorIcon : function(){
20954         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20955     },
20956
20957     /**
20958      * Clear any invalid styles/messages for this field
20959      */
20960     clearInvalid : function(){
20961         if(!this.rendered || this.preventMark){ // not rendered
20962             return;
20963         }
20964         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20965         
20966         obj.el.removeClass(this.invalidClass);
20967         switch(this.msgTarget){
20968             case 'qtip':
20969                 obj.el.dom.qtip = '';
20970                 break;
20971             case 'title':
20972                 this.el.dom.title = '';
20973                 break;
20974             case 'under':
20975                 if(this.errorEl){
20976                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
20977                 }
20978                 break;
20979             case 'side':
20980                 if(this.errorIcon){
20981                     this.errorIcon.dom.qtip = '';
20982                     this.errorIcon.hide();
20983                     this.un('resize', this.alignErrorIcon, this);
20984                 }
20985                 break;
20986             default:
20987                 var t = Roo.getDom(this.msgTarget);
20988                 t.innerHTML = '';
20989                 t.style.display = 'none';
20990                 break;
20991         }
20992         this.fireEvent('valid', this);
20993     },
20994
20995     /**
20996      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
20997      * @return {Mixed} value The field value
20998      */
20999     getRawValue : function(){
21000         var v = this.el.getValue();
21001         
21002         return v;
21003     },
21004
21005     /**
21006      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21007      * @return {Mixed} value The field value
21008      */
21009     getValue : function(){
21010         var v = this.el.getValue();
21011          
21012         return v;
21013     },
21014
21015     /**
21016      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21017      * @param {Mixed} value The value to set
21018      */
21019     setRawValue : function(v){
21020         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21021     },
21022
21023     /**
21024      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21025      * @param {Mixed} value The value to set
21026      */
21027     setValue : function(v){
21028         this.value = v;
21029         if(this.rendered){
21030             this.el.dom.value = (v === null || v === undefined ? '' : v);
21031              this.validate();
21032         }
21033     },
21034
21035     adjustSize : function(w, h){
21036         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21037         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21038         return s;
21039     },
21040
21041     adjustWidth : function(tag, w){
21042         tag = tag.toLowerCase();
21043         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21044             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21045                 if(tag == 'input'){
21046                     return w + 2;
21047                 }
21048                 if(tag == 'textarea'){
21049                     return w-2;
21050                 }
21051             }else if(Roo.isOpera){
21052                 if(tag == 'input'){
21053                     return w + 2;
21054                 }
21055                 if(tag == 'textarea'){
21056                     return w-2;
21057                 }
21058             }
21059         }
21060         return w;
21061     }
21062 });
21063
21064
21065 // anything other than normal should be considered experimental
21066 Roo.form.Field.msgFx = {
21067     normal : {
21068         show: function(msgEl, f){
21069             msgEl.setDisplayed('block');
21070         },
21071
21072         hide : function(msgEl, f){
21073             msgEl.setDisplayed(false).update('');
21074         }
21075     },
21076
21077     slide : {
21078         show: function(msgEl, f){
21079             msgEl.slideIn('t', {stopFx:true});
21080         },
21081
21082         hide : function(msgEl, f){
21083             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21084         }
21085     },
21086
21087     slideRight : {
21088         show: function(msgEl, f){
21089             msgEl.fixDisplay();
21090             msgEl.alignTo(f.el, 'tl-tr');
21091             msgEl.slideIn('l', {stopFx:true});
21092         },
21093
21094         hide : function(msgEl, f){
21095             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21096         }
21097     }
21098 };/*
21099  * Based on:
21100  * Ext JS Library 1.1.1
21101  * Copyright(c) 2006-2007, Ext JS, LLC.
21102  *
21103  * Originally Released Under LGPL - original licence link has changed is not relivant.
21104  *
21105  * Fork - LGPL
21106  * <script type="text/javascript">
21107  */
21108  
21109
21110 /**
21111  * @class Roo.form.TextField
21112  * @extends Roo.form.Field
21113  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21114  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21115  * @constructor
21116  * Creates a new TextField
21117  * @param {Object} config Configuration options
21118  */
21119 Roo.form.TextField = function(config){
21120     Roo.form.TextField.superclass.constructor.call(this, config);
21121     this.addEvents({
21122         /**
21123          * @event autosize
21124          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21125          * according to the default logic, but this event provides a hook for the developer to apply additional
21126          * logic at runtime to resize the field if needed.
21127              * @param {Roo.form.Field} this This text field
21128              * @param {Number} width The new field width
21129              */
21130         autosize : true
21131     });
21132 };
21133
21134 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21135     /**
21136      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21137      */
21138     grow : false,
21139     /**
21140      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21141      */
21142     growMin : 30,
21143     /**
21144      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21145      */
21146     growMax : 800,
21147     /**
21148      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21149      */
21150     vtype : null,
21151     /**
21152      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21153      */
21154     maskRe : null,
21155     /**
21156      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21157      */
21158     disableKeyFilter : false,
21159     /**
21160      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21161      */
21162     allowBlank : true,
21163     /**
21164      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21165      */
21166     minLength : 0,
21167     /**
21168      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21169      */
21170     maxLength : Number.MAX_VALUE,
21171     /**
21172      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21173      */
21174     minLengthText : "The minimum length for this field is {0}",
21175     /**
21176      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21177      */
21178     maxLengthText : "The maximum length for this field is {0}",
21179     /**
21180      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21181      */
21182     selectOnFocus : false,
21183     /**
21184      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21185      */
21186     blankText : "This field is required",
21187     /**
21188      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21189      * If available, this function will be called only after the basic validators all return true, and will be passed the
21190      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21191      */
21192     validator : null,
21193     /**
21194      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21195      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21196      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21197      */
21198     regex : null,
21199     /**
21200      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21201      */
21202     regexText : "",
21203     /**
21204      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21205      */
21206     emptyText : null,
21207    
21208
21209     // private
21210     initEvents : function()
21211     {
21212         if (this.emptyText) {
21213             this.el.attr('placeholder', this.emptyText);
21214         }
21215         
21216         Roo.form.TextField.superclass.initEvents.call(this);
21217         if(this.validationEvent == 'keyup'){
21218             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21219             this.el.on('keyup', this.filterValidation, this);
21220         }
21221         else if(this.validationEvent !== false){
21222             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21223         }
21224         
21225         if(this.selectOnFocus){
21226             this.on("focus", this.preFocus, this);
21227             
21228         }
21229         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21230             this.el.on("keypress", this.filterKeys, this);
21231         }
21232         if(this.grow){
21233             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21234             this.el.on("click", this.autoSize,  this);
21235         }
21236         if(this.el.is('input[type=password]') && Roo.isSafari){
21237             this.el.on('keydown', this.SafariOnKeyDown, this);
21238         }
21239     },
21240
21241     processValue : function(value){
21242         if(this.stripCharsRe){
21243             var newValue = value.replace(this.stripCharsRe, '');
21244             if(newValue !== value){
21245                 this.setRawValue(newValue);
21246                 return newValue;
21247             }
21248         }
21249         return value;
21250     },
21251
21252     filterValidation : function(e){
21253         if(!e.isNavKeyPress()){
21254             this.validationTask.delay(this.validationDelay);
21255         }
21256     },
21257
21258     // private
21259     onKeyUp : function(e){
21260         if(!e.isNavKeyPress()){
21261             this.autoSize();
21262         }
21263     },
21264
21265     /**
21266      * Resets the current field value to the originally-loaded value and clears any validation messages.
21267      *  
21268      */
21269     reset : function(){
21270         Roo.form.TextField.superclass.reset.call(this);
21271        
21272     },
21273
21274     
21275     // private
21276     preFocus : function(){
21277         
21278         if(this.selectOnFocus){
21279             this.el.dom.select();
21280         }
21281     },
21282
21283     
21284     // private
21285     filterKeys : function(e){
21286         var k = e.getKey();
21287         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21288             return;
21289         }
21290         var c = e.getCharCode(), cc = String.fromCharCode(c);
21291         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21292             return;
21293         }
21294         if(!this.maskRe.test(cc)){
21295             e.stopEvent();
21296         }
21297     },
21298
21299     setValue : function(v){
21300         
21301         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21302         
21303         this.autoSize();
21304     },
21305
21306     /**
21307      * Validates a value according to the field's validation rules and marks the field as invalid
21308      * if the validation fails
21309      * @param {Mixed} value The value to validate
21310      * @return {Boolean} True if the value is valid, else false
21311      */
21312     validateValue : function(value){
21313         if(value.length < 1)  { // if it's blank
21314              if(this.allowBlank){
21315                 this.clearInvalid();
21316                 return true;
21317              }else{
21318                 this.markInvalid(this.blankText);
21319                 return false;
21320              }
21321         }
21322         if(value.length < this.minLength){
21323             this.markInvalid(String.format(this.minLengthText, this.minLength));
21324             return false;
21325         }
21326         if(value.length > this.maxLength){
21327             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21328             return false;
21329         }
21330         if(this.vtype){
21331             var vt = Roo.form.VTypes;
21332             if(!vt[this.vtype](value, this)){
21333                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21334                 return false;
21335             }
21336         }
21337         if(typeof this.validator == "function"){
21338             var msg = this.validator(value);
21339             if(msg !== true){
21340                 this.markInvalid(msg);
21341                 return false;
21342             }
21343         }
21344         if(this.regex && !this.regex.test(value)){
21345             this.markInvalid(this.regexText);
21346             return false;
21347         }
21348         return true;
21349     },
21350
21351     /**
21352      * Selects text in this field
21353      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21354      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21355      */
21356     selectText : function(start, end){
21357         var v = this.getRawValue();
21358         if(v.length > 0){
21359             start = start === undefined ? 0 : start;
21360             end = end === undefined ? v.length : end;
21361             var d = this.el.dom;
21362             if(d.setSelectionRange){
21363                 d.setSelectionRange(start, end);
21364             }else if(d.createTextRange){
21365                 var range = d.createTextRange();
21366                 range.moveStart("character", start);
21367                 range.moveEnd("character", v.length-end);
21368                 range.select();
21369             }
21370         }
21371     },
21372
21373     /**
21374      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21375      * This only takes effect if grow = true, and fires the autosize event.
21376      */
21377     autoSize : function(){
21378         if(!this.grow || !this.rendered){
21379             return;
21380         }
21381         if(!this.metrics){
21382             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21383         }
21384         var el = this.el;
21385         var v = el.dom.value;
21386         var d = document.createElement('div');
21387         d.appendChild(document.createTextNode(v));
21388         v = d.innerHTML;
21389         d = null;
21390         v += "&#160;";
21391         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21392         this.el.setWidth(w);
21393         this.fireEvent("autosize", this, w);
21394     },
21395     
21396     // private
21397     SafariOnKeyDown : function(event)
21398     {
21399         // this is a workaround for a password hang bug on chrome/ webkit.
21400         
21401         var isSelectAll = false;
21402         
21403         if(this.el.dom.selectionEnd > 0){
21404             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21405         }
21406         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21407             event.preventDefault();
21408             this.setValue('');
21409             return;
21410         }
21411         
21412         if(isSelectAll){ // backspace and delete key
21413             
21414             event.preventDefault();
21415             // this is very hacky as keydown always get's upper case.
21416             //
21417             var cc = String.fromCharCode(event.getCharCode());
21418             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21419             
21420         }
21421         
21422         
21423     }
21424 });/*
21425  * Based on:
21426  * Ext JS Library 1.1.1
21427  * Copyright(c) 2006-2007, Ext JS, LLC.
21428  *
21429  * Originally Released Under LGPL - original licence link has changed is not relivant.
21430  *
21431  * Fork - LGPL
21432  * <script type="text/javascript">
21433  */
21434  
21435 /**
21436  * @class Roo.form.Hidden
21437  * @extends Roo.form.TextField
21438  * Simple Hidden element used on forms 
21439  * 
21440  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21441  * 
21442  * @constructor
21443  * Creates a new Hidden form element.
21444  * @param {Object} config Configuration options
21445  */
21446
21447
21448
21449 // easy hidden field...
21450 Roo.form.Hidden = function(config){
21451     Roo.form.Hidden.superclass.constructor.call(this, config);
21452 };
21453   
21454 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21455     fieldLabel:      '',
21456     inputType:      'hidden',
21457     width:          50,
21458     allowBlank:     true,
21459     labelSeparator: '',
21460     hidden:         true,
21461     itemCls :       'x-form-item-display-none'
21462
21463
21464 });
21465
21466
21467 /*
21468  * Based on:
21469  * Ext JS Library 1.1.1
21470  * Copyright(c) 2006-2007, Ext JS, LLC.
21471  *
21472  * Originally Released Under LGPL - original licence link has changed is not relivant.
21473  *
21474  * Fork - LGPL
21475  * <script type="text/javascript">
21476  */
21477  
21478 /**
21479  * @class Roo.form.TriggerField
21480  * @extends Roo.form.TextField
21481  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21482  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21483  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21484  * for which you can provide a custom implementation.  For example:
21485  * <pre><code>
21486 var trigger = new Roo.form.TriggerField();
21487 trigger.onTriggerClick = myTriggerFn;
21488 trigger.applyTo('my-field');
21489 </code></pre>
21490  *
21491  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21492  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21493  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21494  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21495  * @constructor
21496  * Create a new TriggerField.
21497  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21498  * to the base TextField)
21499  */
21500 Roo.form.TriggerField = function(config){
21501     this.mimicing = false;
21502     Roo.form.TriggerField.superclass.constructor.call(this, config);
21503 };
21504
21505 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21506     /**
21507      * @cfg {String} triggerClass A CSS class to apply to the trigger
21508      */
21509     /**
21510      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21511      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21512      */
21513     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21514     /**
21515      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21516      */
21517     hideTrigger:false,
21518
21519     /** @cfg {Boolean} grow @hide */
21520     /** @cfg {Number} growMin @hide */
21521     /** @cfg {Number} growMax @hide */
21522
21523     /**
21524      * @hide 
21525      * @method
21526      */
21527     autoSize: Roo.emptyFn,
21528     // private
21529     monitorTab : true,
21530     // private
21531     deferHeight : true,
21532
21533     
21534     actionMode : 'wrap',
21535     // private
21536     onResize : function(w, h){
21537         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21538         if(typeof w == 'number'){
21539             var x = w - this.trigger.getWidth();
21540             this.el.setWidth(this.adjustWidth('input', x));
21541             this.trigger.setStyle('left', x+'px');
21542         }
21543     },
21544
21545     // private
21546     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21547
21548     // private
21549     getResizeEl : function(){
21550         return this.wrap;
21551     },
21552
21553     // private
21554     getPositionEl : function(){
21555         return this.wrap;
21556     },
21557
21558     // private
21559     alignErrorIcon : function(){
21560         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21561     },
21562
21563     // private
21564     onRender : function(ct, position){
21565         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21566         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21567         this.trigger = this.wrap.createChild(this.triggerConfig ||
21568                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21569         if(this.hideTrigger){
21570             this.trigger.setDisplayed(false);
21571         }
21572         this.initTrigger();
21573         if(!this.width){
21574             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21575         }
21576     },
21577
21578     // private
21579     initTrigger : function(){
21580         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21581         this.trigger.addClassOnOver('x-form-trigger-over');
21582         this.trigger.addClassOnClick('x-form-trigger-click');
21583     },
21584
21585     // private
21586     onDestroy : function(){
21587         if(this.trigger){
21588             this.trigger.removeAllListeners();
21589             this.trigger.remove();
21590         }
21591         if(this.wrap){
21592             this.wrap.remove();
21593         }
21594         Roo.form.TriggerField.superclass.onDestroy.call(this);
21595     },
21596
21597     // private
21598     onFocus : function(){
21599         Roo.form.TriggerField.superclass.onFocus.call(this);
21600         if(!this.mimicing){
21601             this.wrap.addClass('x-trigger-wrap-focus');
21602             this.mimicing = true;
21603             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21604             if(this.monitorTab){
21605                 this.el.on("keydown", this.checkTab, this);
21606             }
21607         }
21608     },
21609
21610     // private
21611     checkTab : function(e){
21612         if(e.getKey() == e.TAB){
21613             this.triggerBlur();
21614         }
21615     },
21616
21617     // private
21618     onBlur : function(){
21619         // do nothing
21620     },
21621
21622     // private
21623     mimicBlur : function(e, t){
21624         if(!this.wrap.contains(t) && this.validateBlur()){
21625             this.triggerBlur();
21626         }
21627     },
21628
21629     // private
21630     triggerBlur : function(){
21631         this.mimicing = false;
21632         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21633         if(this.monitorTab){
21634             this.el.un("keydown", this.checkTab, this);
21635         }
21636         this.wrap.removeClass('x-trigger-wrap-focus');
21637         Roo.form.TriggerField.superclass.onBlur.call(this);
21638     },
21639
21640     // private
21641     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21642     validateBlur : function(e, t){
21643         return true;
21644     },
21645
21646     // private
21647     onDisable : function(){
21648         Roo.form.TriggerField.superclass.onDisable.call(this);
21649         if(this.wrap){
21650             this.wrap.addClass('x-item-disabled');
21651         }
21652     },
21653
21654     // private
21655     onEnable : function(){
21656         Roo.form.TriggerField.superclass.onEnable.call(this);
21657         if(this.wrap){
21658             this.wrap.removeClass('x-item-disabled');
21659         }
21660     },
21661
21662     // private
21663     onShow : function(){
21664         var ae = this.getActionEl();
21665         
21666         if(ae){
21667             ae.dom.style.display = '';
21668             ae.dom.style.visibility = 'visible';
21669         }
21670     },
21671
21672     // private
21673     
21674     onHide : function(){
21675         var ae = this.getActionEl();
21676         ae.dom.style.display = 'none';
21677     },
21678
21679     /**
21680      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21681      * by an implementing function.
21682      * @method
21683      * @param {EventObject} e
21684      */
21685     onTriggerClick : Roo.emptyFn
21686 });
21687
21688 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21689 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21690 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21691 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21692     initComponent : function(){
21693         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21694
21695         this.triggerConfig = {
21696             tag:'span', cls:'x-form-twin-triggers', cn:[
21697             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21698             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21699         ]};
21700     },
21701
21702     getTrigger : function(index){
21703         return this.triggers[index];
21704     },
21705
21706     initTrigger : function(){
21707         var ts = this.trigger.select('.x-form-trigger', true);
21708         this.wrap.setStyle('overflow', 'hidden');
21709         var triggerField = this;
21710         ts.each(function(t, all, index){
21711             t.hide = function(){
21712                 var w = triggerField.wrap.getWidth();
21713                 this.dom.style.display = 'none';
21714                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21715             };
21716             t.show = function(){
21717                 var w = triggerField.wrap.getWidth();
21718                 this.dom.style.display = '';
21719                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21720             };
21721             var triggerIndex = 'Trigger'+(index+1);
21722
21723             if(this['hide'+triggerIndex]){
21724                 t.dom.style.display = 'none';
21725             }
21726             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21727             t.addClassOnOver('x-form-trigger-over');
21728             t.addClassOnClick('x-form-trigger-click');
21729         }, this);
21730         this.triggers = ts.elements;
21731     },
21732
21733     onTrigger1Click : Roo.emptyFn,
21734     onTrigger2Click : Roo.emptyFn
21735 });/*
21736  * Based on:
21737  * Ext JS Library 1.1.1
21738  * Copyright(c) 2006-2007, Ext JS, LLC.
21739  *
21740  * Originally Released Under LGPL - original licence link has changed is not relivant.
21741  *
21742  * Fork - LGPL
21743  * <script type="text/javascript">
21744  */
21745  
21746 /**
21747  * @class Roo.form.TextArea
21748  * @extends Roo.form.TextField
21749  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21750  * support for auto-sizing.
21751  * @constructor
21752  * Creates a new TextArea
21753  * @param {Object} config Configuration options
21754  */
21755 Roo.form.TextArea = function(config){
21756     Roo.form.TextArea.superclass.constructor.call(this, config);
21757     // these are provided exchanges for backwards compat
21758     // minHeight/maxHeight were replaced by growMin/growMax to be
21759     // compatible with TextField growing config values
21760     if(this.minHeight !== undefined){
21761         this.growMin = this.minHeight;
21762     }
21763     if(this.maxHeight !== undefined){
21764         this.growMax = this.maxHeight;
21765     }
21766 };
21767
21768 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21769     /**
21770      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21771      */
21772     growMin : 60,
21773     /**
21774      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21775      */
21776     growMax: 1000,
21777     /**
21778      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21779      * in the field (equivalent to setting overflow: hidden, defaults to false)
21780      */
21781     preventScrollbars: false,
21782     /**
21783      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21784      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21785      */
21786
21787     // private
21788     onRender : function(ct, position){
21789         if(!this.el){
21790             this.defaultAutoCreate = {
21791                 tag: "textarea",
21792                 style:"width:300px;height:60px;",
21793                 autocomplete: "off"
21794             };
21795         }
21796         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21797         if(this.grow){
21798             this.textSizeEl = Roo.DomHelper.append(document.body, {
21799                 tag: "pre", cls: "x-form-grow-sizer"
21800             });
21801             if(this.preventScrollbars){
21802                 this.el.setStyle("overflow", "hidden");
21803             }
21804             this.el.setHeight(this.growMin);
21805         }
21806     },
21807
21808     onDestroy : function(){
21809         if(this.textSizeEl){
21810             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21811         }
21812         Roo.form.TextArea.superclass.onDestroy.call(this);
21813     },
21814
21815     // private
21816     onKeyUp : function(e){
21817         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21818             this.autoSize();
21819         }
21820     },
21821
21822     /**
21823      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21824      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21825      */
21826     autoSize : function(){
21827         if(!this.grow || !this.textSizeEl){
21828             return;
21829         }
21830         var el = this.el;
21831         var v = el.dom.value;
21832         var ts = this.textSizeEl;
21833
21834         ts.innerHTML = '';
21835         ts.appendChild(document.createTextNode(v));
21836         v = ts.innerHTML;
21837
21838         Roo.fly(ts).setWidth(this.el.getWidth());
21839         if(v.length < 1){
21840             v = "&#160;&#160;";
21841         }else{
21842             if(Roo.isIE){
21843                 v = v.replace(/\n/g, '<p>&#160;</p>');
21844             }
21845             v += "&#160;\n&#160;";
21846         }
21847         ts.innerHTML = v;
21848         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21849         if(h != this.lastHeight){
21850             this.lastHeight = h;
21851             this.el.setHeight(h);
21852             this.fireEvent("autosize", this, h);
21853         }
21854     }
21855 });/*
21856  * Based on:
21857  * Ext JS Library 1.1.1
21858  * Copyright(c) 2006-2007, Ext JS, LLC.
21859  *
21860  * Originally Released Under LGPL - original licence link has changed is not relivant.
21861  *
21862  * Fork - LGPL
21863  * <script type="text/javascript">
21864  */
21865  
21866
21867 /**
21868  * @class Roo.form.NumberField
21869  * @extends Roo.form.TextField
21870  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21871  * @constructor
21872  * Creates a new NumberField
21873  * @param {Object} config Configuration options
21874  */
21875 Roo.form.NumberField = function(config){
21876     Roo.form.NumberField.superclass.constructor.call(this, config);
21877 };
21878
21879 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21880     /**
21881      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21882      */
21883     fieldClass: "x-form-field x-form-num-field",
21884     /**
21885      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21886      */
21887     allowDecimals : true,
21888     /**
21889      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21890      */
21891     decimalSeparator : ".",
21892     /**
21893      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21894      */
21895     decimalPrecision : 2,
21896     /**
21897      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21898      */
21899     allowNegative : true,
21900     /**
21901      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21902      */
21903     minValue : Number.NEGATIVE_INFINITY,
21904     /**
21905      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21906      */
21907     maxValue : Number.MAX_VALUE,
21908     /**
21909      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21910      */
21911     minText : "The minimum value for this field is {0}",
21912     /**
21913      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21914      */
21915     maxText : "The maximum value for this field is {0}",
21916     /**
21917      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21918      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21919      */
21920     nanText : "{0} is not a valid number",
21921
21922     // private
21923     initEvents : function(){
21924         Roo.form.NumberField.superclass.initEvents.call(this);
21925         var allowed = "0123456789";
21926         if(this.allowDecimals){
21927             allowed += this.decimalSeparator;
21928         }
21929         if(this.allowNegative){
21930             allowed += "-";
21931         }
21932         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21933         var keyPress = function(e){
21934             var k = e.getKey();
21935             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21936                 return;
21937             }
21938             var c = e.getCharCode();
21939             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21940                 e.stopEvent();
21941             }
21942         };
21943         this.el.on("keypress", keyPress, this);
21944     },
21945
21946     // private
21947     validateValue : function(value){
21948         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21949             return false;
21950         }
21951         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21952              return true;
21953         }
21954         var num = this.parseValue(value);
21955         if(isNaN(num)){
21956             this.markInvalid(String.format(this.nanText, value));
21957             return false;
21958         }
21959         if(num < this.minValue){
21960             this.markInvalid(String.format(this.minText, this.minValue));
21961             return false;
21962         }
21963         if(num > this.maxValue){
21964             this.markInvalid(String.format(this.maxText, this.maxValue));
21965             return false;
21966         }
21967         return true;
21968     },
21969
21970     getValue : function(){
21971         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
21972     },
21973
21974     // private
21975     parseValue : function(value){
21976         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
21977         return isNaN(value) ? '' : value;
21978     },
21979
21980     // private
21981     fixPrecision : function(value){
21982         var nan = isNaN(value);
21983         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
21984             return nan ? '' : value;
21985         }
21986         return parseFloat(value).toFixed(this.decimalPrecision);
21987     },
21988
21989     setValue : function(v){
21990         v = this.fixPrecision(v);
21991         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
21992     },
21993
21994     // private
21995     decimalPrecisionFcn : function(v){
21996         return Math.floor(v);
21997     },
21998
21999     beforeBlur : function(){
22000         var v = this.parseValue(this.getRawValue());
22001         if(v){
22002             this.setValue(v);
22003         }
22004     }
22005 });/*
22006  * Based on:
22007  * Ext JS Library 1.1.1
22008  * Copyright(c) 2006-2007, Ext JS, LLC.
22009  *
22010  * Originally Released Under LGPL - original licence link has changed is not relivant.
22011  *
22012  * Fork - LGPL
22013  * <script type="text/javascript">
22014  */
22015  
22016 /**
22017  * @class Roo.form.DateField
22018  * @extends Roo.form.TriggerField
22019  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22020 * @constructor
22021 * Create a new DateField
22022 * @param {Object} config
22023  */
22024 Roo.form.DateField = function(config){
22025     Roo.form.DateField.superclass.constructor.call(this, config);
22026     
22027       this.addEvents({
22028          
22029         /**
22030          * @event select
22031          * Fires when a date is selected
22032              * @param {Roo.form.DateField} combo This combo box
22033              * @param {Date} date The date selected
22034              */
22035         'select' : true
22036          
22037     });
22038     
22039     
22040     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22041     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22042     this.ddMatch = null;
22043     if(this.disabledDates){
22044         var dd = this.disabledDates;
22045         var re = "(?:";
22046         for(var i = 0; i < dd.length; i++){
22047             re += dd[i];
22048             if(i != dd.length-1) re += "|";
22049         }
22050         this.ddMatch = new RegExp(re + ")");
22051     }
22052 };
22053
22054 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22055     /**
22056      * @cfg {String} format
22057      * The default date format string which can be overriden for localization support.  The format must be
22058      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22059      */
22060     format : "m/d/y",
22061     /**
22062      * @cfg {String} altFormats
22063      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22064      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22065      */
22066     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22067     /**
22068      * @cfg {Array} disabledDays
22069      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22070      */
22071     disabledDays : null,
22072     /**
22073      * @cfg {String} disabledDaysText
22074      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22075      */
22076     disabledDaysText : "Disabled",
22077     /**
22078      * @cfg {Array} disabledDates
22079      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22080      * expression so they are very powerful. Some examples:
22081      * <ul>
22082      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22083      * <li>["03/08", "09/16"] would disable those days for every year</li>
22084      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22085      * <li>["03/../2006"] would disable every day in March 2006</li>
22086      * <li>["^03"] would disable every day in every March</li>
22087      * </ul>
22088      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22089      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22090      */
22091     disabledDates : null,
22092     /**
22093      * @cfg {String} disabledDatesText
22094      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22095      */
22096     disabledDatesText : "Disabled",
22097     /**
22098      * @cfg {Date/String} minValue
22099      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22100      * valid format (defaults to null).
22101      */
22102     minValue : null,
22103     /**
22104      * @cfg {Date/String} maxValue
22105      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22106      * valid format (defaults to null).
22107      */
22108     maxValue : null,
22109     /**
22110      * @cfg {String} minText
22111      * The error text to display when the date in the cell is before minValue (defaults to
22112      * 'The date in this field must be after {minValue}').
22113      */
22114     minText : "The date in this field must be equal to or after {0}",
22115     /**
22116      * @cfg {String} maxText
22117      * The error text to display when the date in the cell is after maxValue (defaults to
22118      * 'The date in this field must be before {maxValue}').
22119      */
22120     maxText : "The date in this field must be equal to or before {0}",
22121     /**
22122      * @cfg {String} invalidText
22123      * The error text to display when the date in the field is invalid (defaults to
22124      * '{value} is not a valid date - it must be in the format {format}').
22125      */
22126     invalidText : "{0} is not a valid date - it must be in the format {1}",
22127     /**
22128      * @cfg {String} triggerClass
22129      * An additional CSS class used to style the trigger button.  The trigger will always get the
22130      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22131      * which displays a calendar icon).
22132      */
22133     triggerClass : 'x-form-date-trigger',
22134     
22135
22136     /**
22137      * @cfg {Boolean} useIso
22138      * if enabled, then the date field will use a hidden field to store the 
22139      * real value as iso formated date. default (false)
22140      */ 
22141     useIso : false,
22142     /**
22143      * @cfg {String/Object} autoCreate
22144      * A DomHelper element spec, or true for a default element spec (defaults to
22145      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22146      */ 
22147     // private
22148     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22149     
22150     // private
22151     hiddenField: false,
22152     
22153     onRender : function(ct, position)
22154     {
22155         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22156         if (this.useIso) {
22157             //this.el.dom.removeAttribute('name'); 
22158             Roo.log("Changing name?");
22159             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22160             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22161                     'before', true);
22162             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22163             // prevent input submission
22164             this.hiddenName = this.name;
22165         }
22166             
22167             
22168     },
22169     
22170     // private
22171     validateValue : function(value)
22172     {
22173         value = this.formatDate(value);
22174         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22175             Roo.log('super failed');
22176             return false;
22177         }
22178         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22179              return true;
22180         }
22181         var svalue = value;
22182         value = this.parseDate(value);
22183         if(!value){
22184             Roo.log('parse date failed' + svalue);
22185             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22186             return false;
22187         }
22188         var time = value.getTime();
22189         if(this.minValue && time < this.minValue.getTime()){
22190             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22191             return false;
22192         }
22193         if(this.maxValue && time > this.maxValue.getTime()){
22194             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22195             return false;
22196         }
22197         if(this.disabledDays){
22198             var day = value.getDay();
22199             for(var i = 0; i < this.disabledDays.length; i++) {
22200                 if(day === this.disabledDays[i]){
22201                     this.markInvalid(this.disabledDaysText);
22202                     return false;
22203                 }
22204             }
22205         }
22206         var fvalue = this.formatDate(value);
22207         if(this.ddMatch && this.ddMatch.test(fvalue)){
22208             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22209             return false;
22210         }
22211         return true;
22212     },
22213
22214     // private
22215     // Provides logic to override the default TriggerField.validateBlur which just returns true
22216     validateBlur : function(){
22217         return !this.menu || !this.menu.isVisible();
22218     },
22219     
22220     getName: function()
22221     {
22222         // returns hidden if it's set..
22223         if (!this.rendered) {return ''};
22224         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22225         
22226     },
22227
22228     /**
22229      * Returns the current date value of the date field.
22230      * @return {Date} The date value
22231      */
22232     getValue : function(){
22233         
22234         return  this.hiddenField ?
22235                 this.hiddenField.value :
22236                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22237     },
22238
22239     /**
22240      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22241      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22242      * (the default format used is "m/d/y").
22243      * <br />Usage:
22244      * <pre><code>
22245 //All of these calls set the same date value (May 4, 2006)
22246
22247 //Pass a date object:
22248 var dt = new Date('5/4/06');
22249 dateField.setValue(dt);
22250
22251 //Pass a date string (default format):
22252 dateField.setValue('5/4/06');
22253
22254 //Pass a date string (custom format):
22255 dateField.format = 'Y-m-d';
22256 dateField.setValue('2006-5-4');
22257 </code></pre>
22258      * @param {String/Date} date The date or valid date string
22259      */
22260     setValue : function(date){
22261         if (this.hiddenField) {
22262             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22263         }
22264         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22265         // make sure the value field is always stored as a date..
22266         this.value = this.parseDate(date);
22267         
22268         
22269     },
22270
22271     // private
22272     parseDate : function(value){
22273         if(!value || value instanceof Date){
22274             return value;
22275         }
22276         var v = Date.parseDate(value, this.format);
22277          if (!v && this.useIso) {
22278             v = Date.parseDate(value, 'Y-m-d');
22279         }
22280         if(!v && this.altFormats){
22281             if(!this.altFormatsArray){
22282                 this.altFormatsArray = this.altFormats.split("|");
22283             }
22284             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22285                 v = Date.parseDate(value, this.altFormatsArray[i]);
22286             }
22287         }
22288         return v;
22289     },
22290
22291     // private
22292     formatDate : function(date, fmt){
22293         return (!date || !(date instanceof Date)) ?
22294                date : date.dateFormat(fmt || this.format);
22295     },
22296
22297     // private
22298     menuListeners : {
22299         select: function(m, d){
22300             
22301             this.setValue(d);
22302             this.fireEvent('select', this, d);
22303         },
22304         show : function(){ // retain focus styling
22305             this.onFocus();
22306         },
22307         hide : function(){
22308             this.focus.defer(10, this);
22309             var ml = this.menuListeners;
22310             this.menu.un("select", ml.select,  this);
22311             this.menu.un("show", ml.show,  this);
22312             this.menu.un("hide", ml.hide,  this);
22313         }
22314     },
22315
22316     // private
22317     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22318     onTriggerClick : function(){
22319         if(this.disabled){
22320             return;
22321         }
22322         if(this.menu == null){
22323             this.menu = new Roo.menu.DateMenu();
22324         }
22325         Roo.apply(this.menu.picker,  {
22326             showClear: this.allowBlank,
22327             minDate : this.minValue,
22328             maxDate : this.maxValue,
22329             disabledDatesRE : this.ddMatch,
22330             disabledDatesText : this.disabledDatesText,
22331             disabledDays : this.disabledDays,
22332             disabledDaysText : this.disabledDaysText,
22333             format : this.useIso ? 'Y-m-d' : this.format,
22334             minText : String.format(this.minText, this.formatDate(this.minValue)),
22335             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22336         });
22337         this.menu.on(Roo.apply({}, this.menuListeners, {
22338             scope:this
22339         }));
22340         this.menu.picker.setValue(this.getValue() || new Date());
22341         this.menu.show(this.el, "tl-bl?");
22342     },
22343
22344     beforeBlur : function(){
22345         var v = this.parseDate(this.getRawValue());
22346         if(v){
22347             this.setValue(v);
22348         }
22349     },
22350
22351     /*@
22352      * overide
22353      * 
22354      */
22355     isDirty : function() {
22356         if(this.disabled) {
22357             return false;
22358         }
22359         
22360         if(typeof(this.startValue) === 'undefined'){
22361             return false;
22362         }
22363         
22364         return String(this.getValue()) !== String(this.startValue);
22365         
22366     }
22367 });/*
22368  * Based on:
22369  * Ext JS Library 1.1.1
22370  * Copyright(c) 2006-2007, Ext JS, LLC.
22371  *
22372  * Originally Released Under LGPL - original licence link has changed is not relivant.
22373  *
22374  * Fork - LGPL
22375  * <script type="text/javascript">
22376  */
22377  
22378 /**
22379  * @class Roo.form.MonthField
22380  * @extends Roo.form.TriggerField
22381  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22382 * @constructor
22383 * Create a new MonthField
22384 * @param {Object} config
22385  */
22386 Roo.form.MonthField = function(config){
22387     
22388     Roo.form.MonthField.superclass.constructor.call(this, config);
22389     
22390       this.addEvents({
22391          
22392         /**
22393          * @event select
22394          * Fires when a date is selected
22395              * @param {Roo.form.MonthFieeld} combo This combo box
22396              * @param {Date} date The date selected
22397              */
22398         'select' : true
22399          
22400     });
22401     
22402     
22403     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22404     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22405     this.ddMatch = null;
22406     if(this.disabledDates){
22407         var dd = this.disabledDates;
22408         var re = "(?:";
22409         for(var i = 0; i < dd.length; i++){
22410             re += dd[i];
22411             if(i != dd.length-1) re += "|";
22412         }
22413         this.ddMatch = new RegExp(re + ")");
22414     }
22415 };
22416
22417 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22418     /**
22419      * @cfg {String} format
22420      * The default date format string which can be overriden for localization support.  The format must be
22421      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22422      */
22423     format : "M Y",
22424     /**
22425      * @cfg {String} altFormats
22426      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22427      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22428      */
22429     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22430     /**
22431      * @cfg {Array} disabledDays
22432      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22433      */
22434     disabledDays : [0,1,2,3,4,5,6],
22435     /**
22436      * @cfg {String} disabledDaysText
22437      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22438      */
22439     disabledDaysText : "Disabled",
22440     /**
22441      * @cfg {Array} disabledDates
22442      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22443      * expression so they are very powerful. Some examples:
22444      * <ul>
22445      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22446      * <li>["03/08", "09/16"] would disable those days for every year</li>
22447      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22448      * <li>["03/../2006"] would disable every day in March 2006</li>
22449      * <li>["^03"] would disable every day in every March</li>
22450      * </ul>
22451      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22452      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22453      */
22454     disabledDates : null,
22455     /**
22456      * @cfg {String} disabledDatesText
22457      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22458      */
22459     disabledDatesText : "Disabled",
22460     /**
22461      * @cfg {Date/String} minValue
22462      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22463      * valid format (defaults to null).
22464      */
22465     minValue : null,
22466     /**
22467      * @cfg {Date/String} maxValue
22468      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22469      * valid format (defaults to null).
22470      */
22471     maxValue : null,
22472     /**
22473      * @cfg {String} minText
22474      * The error text to display when the date in the cell is before minValue (defaults to
22475      * 'The date in this field must be after {minValue}').
22476      */
22477     minText : "The date in this field must be equal to or after {0}",
22478     /**
22479      * @cfg {String} maxTextf
22480      * The error text to display when the date in the cell is after maxValue (defaults to
22481      * 'The date in this field must be before {maxValue}').
22482      */
22483     maxText : "The date in this field must be equal to or before {0}",
22484     /**
22485      * @cfg {String} invalidText
22486      * The error text to display when the date in the field is invalid (defaults to
22487      * '{value} is not a valid date - it must be in the format {format}').
22488      */
22489     invalidText : "{0} is not a valid date - it must be in the format {1}",
22490     /**
22491      * @cfg {String} triggerClass
22492      * An additional CSS class used to style the trigger button.  The trigger will always get the
22493      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22494      * which displays a calendar icon).
22495      */
22496     triggerClass : 'x-form-date-trigger',
22497     
22498
22499     /**
22500      * @cfg {Boolean} useIso
22501      * if enabled, then the date field will use a hidden field to store the 
22502      * real value as iso formated date. default (true)
22503      */ 
22504     useIso : true,
22505     /**
22506      * @cfg {String/Object} autoCreate
22507      * A DomHelper element spec, or true for a default element spec (defaults to
22508      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22509      */ 
22510     // private
22511     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22512     
22513     // private
22514     hiddenField: false,
22515     
22516     hideMonthPicker : false,
22517     
22518     onRender : function(ct, position)
22519     {
22520         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22521         if (this.useIso) {
22522             this.el.dom.removeAttribute('name'); 
22523             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22524                     'before', true);
22525             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22526             // prevent input submission
22527             this.hiddenName = this.name;
22528         }
22529             
22530             
22531     },
22532     
22533     // private
22534     validateValue : function(value)
22535     {
22536         value = this.formatDate(value);
22537         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22538             return false;
22539         }
22540         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22541              return true;
22542         }
22543         var svalue = value;
22544         value = this.parseDate(value);
22545         if(!value){
22546             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22547             return false;
22548         }
22549         var time = value.getTime();
22550         if(this.minValue && time < this.minValue.getTime()){
22551             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22552             return false;
22553         }
22554         if(this.maxValue && time > this.maxValue.getTime()){
22555             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22556             return false;
22557         }
22558         /*if(this.disabledDays){
22559             var day = value.getDay();
22560             for(var i = 0; i < this.disabledDays.length; i++) {
22561                 if(day === this.disabledDays[i]){
22562                     this.markInvalid(this.disabledDaysText);
22563                     return false;
22564                 }
22565             }
22566         }
22567         */
22568         var fvalue = this.formatDate(value);
22569         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22570             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22571             return false;
22572         }
22573         */
22574         return true;
22575     },
22576
22577     // private
22578     // Provides logic to override the default TriggerField.validateBlur which just returns true
22579     validateBlur : function(){
22580         return !this.menu || !this.menu.isVisible();
22581     },
22582
22583     /**
22584      * Returns the current date value of the date field.
22585      * @return {Date} The date value
22586      */
22587     getValue : function(){
22588         
22589         
22590         
22591         return  this.hiddenField ?
22592                 this.hiddenField.value :
22593                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22594     },
22595
22596     /**
22597      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22598      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22599      * (the default format used is "m/d/y").
22600      * <br />Usage:
22601      * <pre><code>
22602 //All of these calls set the same date value (May 4, 2006)
22603
22604 //Pass a date object:
22605 var dt = new Date('5/4/06');
22606 monthField.setValue(dt);
22607
22608 //Pass a date string (default format):
22609 monthField.setValue('5/4/06');
22610
22611 //Pass a date string (custom format):
22612 monthField.format = 'Y-m-d';
22613 monthField.setValue('2006-5-4');
22614 </code></pre>
22615      * @param {String/Date} date The date or valid date string
22616      */
22617     setValue : function(date){
22618         Roo.log('month setValue' + date);
22619         // can only be first of month..
22620         
22621         var val = this.parseDate(date);
22622         
22623         if (this.hiddenField) {
22624             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22625         }
22626         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22627         this.value = this.parseDate(date);
22628     },
22629
22630     // private
22631     parseDate : function(value){
22632         if(!value || value instanceof Date){
22633             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22634             return value;
22635         }
22636         var v = Date.parseDate(value, this.format);
22637         if (!v && this.useIso) {
22638             v = Date.parseDate(value, 'Y-m-d');
22639         }
22640         if (v) {
22641             // 
22642             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22643         }
22644         
22645         
22646         if(!v && this.altFormats){
22647             if(!this.altFormatsArray){
22648                 this.altFormatsArray = this.altFormats.split("|");
22649             }
22650             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22651                 v = Date.parseDate(value, this.altFormatsArray[i]);
22652             }
22653         }
22654         return v;
22655     },
22656
22657     // private
22658     formatDate : function(date, fmt){
22659         return (!date || !(date instanceof Date)) ?
22660                date : date.dateFormat(fmt || this.format);
22661     },
22662
22663     // private
22664     menuListeners : {
22665         select: function(m, d){
22666             this.setValue(d);
22667             this.fireEvent('select', this, d);
22668         },
22669         show : function(){ // retain focus styling
22670             this.onFocus();
22671         },
22672         hide : function(){
22673             this.focus.defer(10, this);
22674             var ml = this.menuListeners;
22675             this.menu.un("select", ml.select,  this);
22676             this.menu.un("show", ml.show,  this);
22677             this.menu.un("hide", ml.hide,  this);
22678         }
22679     },
22680     // private
22681     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22682     onTriggerClick : function(){
22683         if(this.disabled){
22684             return;
22685         }
22686         if(this.menu == null){
22687             this.menu = new Roo.menu.DateMenu();
22688            
22689         }
22690         
22691         Roo.apply(this.menu.picker,  {
22692             
22693             showClear: this.allowBlank,
22694             minDate : this.minValue,
22695             maxDate : this.maxValue,
22696             disabledDatesRE : this.ddMatch,
22697             disabledDatesText : this.disabledDatesText,
22698             
22699             format : this.useIso ? 'Y-m-d' : this.format,
22700             minText : String.format(this.minText, this.formatDate(this.minValue)),
22701             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22702             
22703         });
22704          this.menu.on(Roo.apply({}, this.menuListeners, {
22705             scope:this
22706         }));
22707        
22708         
22709         var m = this.menu;
22710         var p = m.picker;
22711         
22712         // hide month picker get's called when we called by 'before hide';
22713         
22714         var ignorehide = true;
22715         p.hideMonthPicker  = function(disableAnim){
22716             if (ignorehide) {
22717                 return;
22718             }
22719              if(this.monthPicker){
22720                 Roo.log("hideMonthPicker called");
22721                 if(disableAnim === true){
22722                     this.monthPicker.hide();
22723                 }else{
22724                     this.monthPicker.slideOut('t', {duration:.2});
22725                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22726                     p.fireEvent("select", this, this.value);
22727                     m.hide();
22728                 }
22729             }
22730         }
22731         
22732         Roo.log('picker set value');
22733         Roo.log(this.getValue());
22734         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22735         m.show(this.el, 'tl-bl?');
22736         ignorehide  = false;
22737         // this will trigger hideMonthPicker..
22738         
22739         
22740         // hidden the day picker
22741         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22742         
22743         
22744         
22745       
22746         
22747         p.showMonthPicker.defer(100, p);
22748     
22749         
22750        
22751     },
22752
22753     beforeBlur : function(){
22754         var v = this.parseDate(this.getRawValue());
22755         if(v){
22756             this.setValue(v);
22757         }
22758     }
22759
22760     /** @cfg {Boolean} grow @hide */
22761     /** @cfg {Number} growMin @hide */
22762     /** @cfg {Number} growMax @hide */
22763     /**
22764      * @hide
22765      * @method autoSize
22766      */
22767 });/*
22768  * Based on:
22769  * Ext JS Library 1.1.1
22770  * Copyright(c) 2006-2007, Ext JS, LLC.
22771  *
22772  * Originally Released Under LGPL - original licence link has changed is not relivant.
22773  *
22774  * Fork - LGPL
22775  * <script type="text/javascript">
22776  */
22777  
22778
22779 /**
22780  * @class Roo.form.ComboBox
22781  * @extends Roo.form.TriggerField
22782  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22783  * @constructor
22784  * Create a new ComboBox.
22785  * @param {Object} config Configuration options
22786  */
22787 Roo.form.ComboBox = function(config){
22788     Roo.form.ComboBox.superclass.constructor.call(this, config);
22789     this.addEvents({
22790         /**
22791          * @event expand
22792          * Fires when the dropdown list is expanded
22793              * @param {Roo.form.ComboBox} combo This combo box
22794              */
22795         'expand' : true,
22796         /**
22797          * @event collapse
22798          * Fires when the dropdown list is collapsed
22799              * @param {Roo.form.ComboBox} combo This combo box
22800              */
22801         'collapse' : true,
22802         /**
22803          * @event beforeselect
22804          * Fires before a list item is selected. Return false to cancel the selection.
22805              * @param {Roo.form.ComboBox} combo This combo box
22806              * @param {Roo.data.Record} record The data record returned from the underlying store
22807              * @param {Number} index The index of the selected item in the dropdown list
22808              */
22809         'beforeselect' : true,
22810         /**
22811          * @event select
22812          * Fires when a list item is selected
22813              * @param {Roo.form.ComboBox} combo This combo box
22814              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22815              * @param {Number} index The index of the selected item in the dropdown list
22816              */
22817         'select' : true,
22818         /**
22819          * @event beforequery
22820          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22821          * The event object passed has these properties:
22822              * @param {Roo.form.ComboBox} combo This combo box
22823              * @param {String} query The query
22824              * @param {Boolean} forceAll true to force "all" query
22825              * @param {Boolean} cancel true to cancel the query
22826              * @param {Object} e The query event object
22827              */
22828         'beforequery': true,
22829          /**
22830          * @event add
22831          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22832              * @param {Roo.form.ComboBox} combo This combo box
22833              */
22834         'add' : true,
22835         /**
22836          * @event edit
22837          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22838              * @param {Roo.form.ComboBox} combo This combo box
22839              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22840              */
22841         'edit' : true
22842         
22843         
22844     });
22845     if(this.transform){
22846         this.allowDomMove = false;
22847         var s = Roo.getDom(this.transform);
22848         if(!this.hiddenName){
22849             this.hiddenName = s.name;
22850         }
22851         if(!this.store){
22852             this.mode = 'local';
22853             var d = [], opts = s.options;
22854             for(var i = 0, len = opts.length;i < len; i++){
22855                 var o = opts[i];
22856                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22857                 if(o.selected) {
22858                     this.value = value;
22859                 }
22860                 d.push([value, o.text]);
22861             }
22862             this.store = new Roo.data.SimpleStore({
22863                 'id': 0,
22864                 fields: ['value', 'text'],
22865                 data : d
22866             });
22867             this.valueField = 'value';
22868             this.displayField = 'text';
22869         }
22870         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22871         if(!this.lazyRender){
22872             this.target = true;
22873             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22874             s.parentNode.removeChild(s); // remove it
22875             this.render(this.el.parentNode);
22876         }else{
22877             s.parentNode.removeChild(s); // remove it
22878         }
22879
22880     }
22881     if (this.store) {
22882         this.store = Roo.factory(this.store, Roo.data);
22883     }
22884     
22885     this.selectedIndex = -1;
22886     if(this.mode == 'local'){
22887         if(config.queryDelay === undefined){
22888             this.queryDelay = 10;
22889         }
22890         if(config.minChars === undefined){
22891             this.minChars = 0;
22892         }
22893     }
22894 };
22895
22896 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22897     /**
22898      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22899      */
22900     /**
22901      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22902      * rendering into an Roo.Editor, defaults to false)
22903      */
22904     /**
22905      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22906      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22907      */
22908     /**
22909      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22910      */
22911     /**
22912      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22913      * the dropdown list (defaults to undefined, with no header element)
22914      */
22915
22916      /**
22917      * @cfg {String/Roo.Template} tpl The template to use to render the output
22918      */
22919      
22920     // private
22921     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22922     /**
22923      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22924      */
22925     listWidth: undefined,
22926     /**
22927      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22928      * mode = 'remote' or 'text' if mode = 'local')
22929      */
22930     displayField: undefined,
22931     /**
22932      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22933      * mode = 'remote' or 'value' if mode = 'local'). 
22934      * Note: use of a valueField requires the user make a selection
22935      * in order for a value to be mapped.
22936      */
22937     valueField: undefined,
22938     
22939     
22940     /**
22941      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22942      * field's data value (defaults to the underlying DOM element's name)
22943      */
22944     hiddenName: undefined,
22945     /**
22946      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22947      */
22948     listClass: '',
22949     /**
22950      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22951      */
22952     selectedClass: 'x-combo-selected',
22953     /**
22954      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22955      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22956      * which displays a downward arrow icon).
22957      */
22958     triggerClass : 'x-form-arrow-trigger',
22959     /**
22960      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22961      */
22962     shadow:'sides',
22963     /**
22964      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22965      * anchor positions (defaults to 'tl-bl')
22966      */
22967     listAlign: 'tl-bl?',
22968     /**
22969      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22970      */
22971     maxHeight: 300,
22972     /**
22973      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22974      * query specified by the allQuery config option (defaults to 'query')
22975      */
22976     triggerAction: 'query',
22977     /**
22978      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22979      * (defaults to 4, does not apply if editable = false)
22980      */
22981     minChars : 4,
22982     /**
22983      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22984      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22985      */
22986     typeAhead: false,
22987     /**
22988      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22989      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22990      */
22991     queryDelay: 500,
22992     /**
22993      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22994      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22995      */
22996     pageSize: 0,
22997     /**
22998      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22999      * when editable = true (defaults to false)
23000      */
23001     selectOnFocus:false,
23002     /**
23003      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23004      */
23005     queryParam: 'query',
23006     /**
23007      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23008      * when mode = 'remote' (defaults to 'Loading...')
23009      */
23010     loadingText: 'Loading...',
23011     /**
23012      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23013      */
23014     resizable: false,
23015     /**
23016      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23017      */
23018     handleHeight : 8,
23019     /**
23020      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23021      * traditional select (defaults to true)
23022      */
23023     editable: true,
23024     /**
23025      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23026      */
23027     allQuery: '',
23028     /**
23029      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23030      */
23031     mode: 'remote',
23032     /**
23033      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23034      * listWidth has a higher value)
23035      */
23036     minListWidth : 70,
23037     /**
23038      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23039      * allow the user to set arbitrary text into the field (defaults to false)
23040      */
23041     forceSelection:false,
23042     /**
23043      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23044      * if typeAhead = true (defaults to 250)
23045      */
23046     typeAheadDelay : 250,
23047     /**
23048      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23049      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23050      */
23051     valueNotFoundText : undefined,
23052     /**
23053      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23054      */
23055     blockFocus : false,
23056     
23057     /**
23058      * @cfg {Boolean} disableClear Disable showing of clear button.
23059      */
23060     disableClear : false,
23061     /**
23062      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23063      */
23064     alwaysQuery : false,
23065     
23066     //private
23067     addicon : false,
23068     editicon: false,
23069     
23070     // element that contains real text value.. (when hidden is used..)
23071      
23072     // private
23073     onRender : function(ct, position){
23074         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23075         if(this.hiddenName){
23076             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23077                     'before', true);
23078             this.hiddenField.value =
23079                 this.hiddenValue !== undefined ? this.hiddenValue :
23080                 this.value !== undefined ? this.value : '';
23081
23082             // prevent input submission
23083             this.el.dom.removeAttribute('name');
23084              
23085              
23086         }
23087         if(Roo.isGecko){
23088             this.el.dom.setAttribute('autocomplete', 'off');
23089         }
23090
23091         var cls = 'x-combo-list';
23092
23093         this.list = new Roo.Layer({
23094             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23095         });
23096
23097         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23098         this.list.setWidth(lw);
23099         this.list.swallowEvent('mousewheel');
23100         this.assetHeight = 0;
23101
23102         if(this.title){
23103             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23104             this.assetHeight += this.header.getHeight();
23105         }
23106
23107         this.innerList = this.list.createChild({cls:cls+'-inner'});
23108         this.innerList.on('mouseover', this.onViewOver, this);
23109         this.innerList.on('mousemove', this.onViewMove, this);
23110         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23111         
23112         if(this.allowBlank && !this.pageSize && !this.disableClear){
23113             this.footer = this.list.createChild({cls:cls+'-ft'});
23114             this.pageTb = new Roo.Toolbar(this.footer);
23115            
23116         }
23117         if(this.pageSize){
23118             this.footer = this.list.createChild({cls:cls+'-ft'});
23119             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23120                     {pageSize: this.pageSize});
23121             
23122         }
23123         
23124         if (this.pageTb && this.allowBlank && !this.disableClear) {
23125             var _this = this;
23126             this.pageTb.add(new Roo.Toolbar.Fill(), {
23127                 cls: 'x-btn-icon x-btn-clear',
23128                 text: '&#160;',
23129                 handler: function()
23130                 {
23131                     _this.collapse();
23132                     _this.clearValue();
23133                     _this.onSelect(false, -1);
23134                 }
23135             });
23136         }
23137         if (this.footer) {
23138             this.assetHeight += this.footer.getHeight();
23139         }
23140         
23141
23142         if(!this.tpl){
23143             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23144         }
23145
23146         this.view = new Roo.View(this.innerList, this.tpl, {
23147             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23148         });
23149
23150         this.view.on('click', this.onViewClick, this);
23151
23152         this.store.on('beforeload', this.onBeforeLoad, this);
23153         this.store.on('load', this.onLoad, this);
23154         this.store.on('loadexception', this.onLoadException, this);
23155
23156         if(this.resizable){
23157             this.resizer = new Roo.Resizable(this.list,  {
23158                pinned:true, handles:'se'
23159             });
23160             this.resizer.on('resize', function(r, w, h){
23161                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23162                 this.listWidth = w;
23163                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23164                 this.restrictHeight();
23165             }, this);
23166             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23167         }
23168         if(!this.editable){
23169             this.editable = true;
23170             this.setEditable(false);
23171         }  
23172         
23173         
23174         if (typeof(this.events.add.listeners) != 'undefined') {
23175             
23176             this.addicon = this.wrap.createChild(
23177                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23178        
23179             this.addicon.on('click', function(e) {
23180                 this.fireEvent('add', this);
23181             }, this);
23182         }
23183         if (typeof(this.events.edit.listeners) != 'undefined') {
23184             
23185             this.editicon = this.wrap.createChild(
23186                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23187             if (this.addicon) {
23188                 this.editicon.setStyle('margin-left', '40px');
23189             }
23190             this.editicon.on('click', function(e) {
23191                 
23192                 // we fire even  if inothing is selected..
23193                 this.fireEvent('edit', this, this.lastData );
23194                 
23195             }, this);
23196         }
23197         
23198         
23199         
23200     },
23201
23202     // private
23203     initEvents : function(){
23204         Roo.form.ComboBox.superclass.initEvents.call(this);
23205
23206         this.keyNav = new Roo.KeyNav(this.el, {
23207             "up" : function(e){
23208                 this.inKeyMode = true;
23209                 this.selectPrev();
23210             },
23211
23212             "down" : function(e){
23213                 if(!this.isExpanded()){
23214                     this.onTriggerClick();
23215                 }else{
23216                     this.inKeyMode = true;
23217                     this.selectNext();
23218                 }
23219             },
23220
23221             "enter" : function(e){
23222                 this.onViewClick();
23223                 //return true;
23224             },
23225
23226             "esc" : function(e){
23227                 this.collapse();
23228             },
23229
23230             "tab" : function(e){
23231                 this.onViewClick(false);
23232                 this.fireEvent("specialkey", this, e);
23233                 return true;
23234             },
23235
23236             scope : this,
23237
23238             doRelay : function(foo, bar, hname){
23239                 if(hname == 'down' || this.scope.isExpanded()){
23240                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23241                 }
23242                 return true;
23243             },
23244
23245             forceKeyDown: true
23246         });
23247         this.queryDelay = Math.max(this.queryDelay || 10,
23248                 this.mode == 'local' ? 10 : 250);
23249         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23250         if(this.typeAhead){
23251             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23252         }
23253         if(this.editable !== false){
23254             this.el.on("keyup", this.onKeyUp, this);
23255         }
23256         if(this.forceSelection){
23257             this.on('blur', this.doForce, this);
23258         }
23259     },
23260
23261     onDestroy : function(){
23262         if(this.view){
23263             this.view.setStore(null);
23264             this.view.el.removeAllListeners();
23265             this.view.el.remove();
23266             this.view.purgeListeners();
23267         }
23268         if(this.list){
23269             this.list.destroy();
23270         }
23271         if(this.store){
23272             this.store.un('beforeload', this.onBeforeLoad, this);
23273             this.store.un('load', this.onLoad, this);
23274             this.store.un('loadexception', this.onLoadException, this);
23275         }
23276         Roo.form.ComboBox.superclass.onDestroy.call(this);
23277     },
23278
23279     // private
23280     fireKey : function(e){
23281         if(e.isNavKeyPress() && !this.list.isVisible()){
23282             this.fireEvent("specialkey", this, e);
23283         }
23284     },
23285
23286     // private
23287     onResize: function(w, h){
23288         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23289         
23290         if(typeof w != 'number'){
23291             // we do not handle it!?!?
23292             return;
23293         }
23294         var tw = this.trigger.getWidth();
23295         tw += this.addicon ? this.addicon.getWidth() : 0;
23296         tw += this.editicon ? this.editicon.getWidth() : 0;
23297         var x = w - tw;
23298         this.el.setWidth( this.adjustWidth('input', x));
23299             
23300         this.trigger.setStyle('left', x+'px');
23301         
23302         if(this.list && this.listWidth === undefined){
23303             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23304             this.list.setWidth(lw);
23305             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23306         }
23307         
23308     
23309         
23310     },
23311
23312     /**
23313      * Allow or prevent the user from directly editing the field text.  If false is passed,
23314      * the user will only be able to select from the items defined in the dropdown list.  This method
23315      * is the runtime equivalent of setting the 'editable' config option at config time.
23316      * @param {Boolean} value True to allow the user to directly edit the field text
23317      */
23318     setEditable : function(value){
23319         if(value == this.editable){
23320             return;
23321         }
23322         this.editable = value;
23323         if(!value){
23324             this.el.dom.setAttribute('readOnly', true);
23325             this.el.on('mousedown', this.onTriggerClick,  this);
23326             this.el.addClass('x-combo-noedit');
23327         }else{
23328             this.el.dom.setAttribute('readOnly', false);
23329             this.el.un('mousedown', this.onTriggerClick,  this);
23330             this.el.removeClass('x-combo-noedit');
23331         }
23332     },
23333
23334     // private
23335     onBeforeLoad : function(){
23336         if(!this.hasFocus){
23337             return;
23338         }
23339         this.innerList.update(this.loadingText ?
23340                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23341         this.restrictHeight();
23342         this.selectedIndex = -1;
23343     },
23344
23345     // private
23346     onLoad : function(){
23347         if(!this.hasFocus){
23348             return;
23349         }
23350         if(this.store.getCount() > 0){
23351             this.expand();
23352             this.restrictHeight();
23353             if(this.lastQuery == this.allQuery){
23354                 if(this.editable){
23355                     this.el.dom.select();
23356                 }
23357                 if(!this.selectByValue(this.value, true)){
23358                     this.select(0, true);
23359                 }
23360             }else{
23361                 this.selectNext();
23362                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23363                     this.taTask.delay(this.typeAheadDelay);
23364                 }
23365             }
23366         }else{
23367             this.onEmptyResults();
23368         }
23369         //this.el.focus();
23370     },
23371     // private
23372     onLoadException : function()
23373     {
23374         this.collapse();
23375         Roo.log(this.store.reader.jsonData);
23376         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23377             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23378         }
23379         
23380         
23381     },
23382     // private
23383     onTypeAhead : function(){
23384         if(this.store.getCount() > 0){
23385             var r = this.store.getAt(0);
23386             var newValue = r.data[this.displayField];
23387             var len = newValue.length;
23388             var selStart = this.getRawValue().length;
23389             if(selStart != len){
23390                 this.setRawValue(newValue);
23391                 this.selectText(selStart, newValue.length);
23392             }
23393         }
23394     },
23395
23396     // private
23397     onSelect : function(record, index){
23398         if(this.fireEvent('beforeselect', this, record, index) !== false){
23399             this.setFromData(index > -1 ? record.data : false);
23400             this.collapse();
23401             this.fireEvent('select', this, record, index);
23402         }
23403     },
23404
23405     /**
23406      * Returns the currently selected field value or empty string if no value is set.
23407      * @return {String} value The selected value
23408      */
23409     getValue : function(){
23410         if(this.valueField){
23411             return typeof this.value != 'undefined' ? this.value : '';
23412         }
23413         return Roo.form.ComboBox.superclass.getValue.call(this);
23414     },
23415
23416     /**
23417      * Clears any text/value currently set in the field
23418      */
23419     clearValue : function(){
23420         if(this.hiddenField){
23421             this.hiddenField.value = '';
23422         }
23423         this.value = '';
23424         this.setRawValue('');
23425         this.lastSelectionText = '';
23426         
23427     },
23428
23429     /**
23430      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23431      * will be displayed in the field.  If the value does not match the data value of an existing item,
23432      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23433      * Otherwise the field will be blank (although the value will still be set).
23434      * @param {String} value The value to match
23435      */
23436     setValue : function(v){
23437         var text = v;
23438         if(this.valueField){
23439             var r = this.findRecord(this.valueField, v);
23440             if(r){
23441                 text = r.data[this.displayField];
23442             }else if(this.valueNotFoundText !== undefined){
23443                 text = this.valueNotFoundText;
23444             }
23445         }
23446         this.lastSelectionText = text;
23447         if(this.hiddenField){
23448             this.hiddenField.value = v;
23449         }
23450         Roo.form.ComboBox.superclass.setValue.call(this, text);
23451         this.value = v;
23452     },
23453     /**
23454      * @property {Object} the last set data for the element
23455      */
23456     
23457     lastData : false,
23458     /**
23459      * Sets the value of the field based on a object which is related to the record format for the store.
23460      * @param {Object} value the value to set as. or false on reset?
23461      */
23462     setFromData : function(o){
23463         var dv = ''; // display value
23464         var vv = ''; // value value..
23465         this.lastData = o;
23466         if (this.displayField) {
23467             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23468         } else {
23469             // this is an error condition!!!
23470             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23471         }
23472         
23473         if(this.valueField){
23474             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23475         }
23476         if(this.hiddenField){
23477             this.hiddenField.value = vv;
23478             
23479             this.lastSelectionText = dv;
23480             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23481             this.value = vv;
23482             return;
23483         }
23484         // no hidden field.. - we store the value in 'value', but still display
23485         // display field!!!!
23486         this.lastSelectionText = dv;
23487         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23488         this.value = vv;
23489         
23490         
23491     },
23492     // private
23493     reset : function(){
23494         // overridden so that last data is reset..
23495         this.setValue(this.resetValue);
23496         this.clearInvalid();
23497         this.lastData = false;
23498         if (this.view) {
23499             this.view.clearSelections();
23500         }
23501     },
23502     // private
23503     findRecord : function(prop, value){
23504         var record;
23505         if(this.store.getCount() > 0){
23506             this.store.each(function(r){
23507                 if(r.data[prop] == value){
23508                     record = r;
23509                     return false;
23510                 }
23511                 return true;
23512             });
23513         }
23514         return record;
23515     },
23516     
23517     getName: function()
23518     {
23519         // returns hidden if it's set..
23520         if (!this.rendered) {return ''};
23521         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23522         
23523     },
23524     // private
23525     onViewMove : function(e, t){
23526         this.inKeyMode = false;
23527     },
23528
23529     // private
23530     onViewOver : function(e, t){
23531         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23532             return;
23533         }
23534         var item = this.view.findItemFromChild(t);
23535         if(item){
23536             var index = this.view.indexOf(item);
23537             this.select(index, false);
23538         }
23539     },
23540
23541     // private
23542     onViewClick : function(doFocus)
23543     {
23544         var index = this.view.getSelectedIndexes()[0];
23545         var r = this.store.getAt(index);
23546         if(r){
23547             this.onSelect(r, index);
23548         }
23549         if(doFocus !== false && !this.blockFocus){
23550             this.el.focus();
23551         }
23552     },
23553
23554     // private
23555     restrictHeight : function(){
23556         this.innerList.dom.style.height = '';
23557         var inner = this.innerList.dom;
23558         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23559         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23560         this.list.beginUpdate();
23561         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23562         this.list.alignTo(this.el, this.listAlign);
23563         this.list.endUpdate();
23564     },
23565
23566     // private
23567     onEmptyResults : function(){
23568         this.collapse();
23569     },
23570
23571     /**
23572      * Returns true if the dropdown list is expanded, else false.
23573      */
23574     isExpanded : function(){
23575         return this.list.isVisible();
23576     },
23577
23578     /**
23579      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23580      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23581      * @param {String} value The data value of the item to select
23582      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23583      * selected item if it is not currently in view (defaults to true)
23584      * @return {Boolean} True if the value matched an item in the list, else false
23585      */
23586     selectByValue : function(v, scrollIntoView){
23587         if(v !== undefined && v !== null){
23588             var r = this.findRecord(this.valueField || this.displayField, v);
23589             if(r){
23590                 this.select(this.store.indexOf(r), scrollIntoView);
23591                 return true;
23592             }
23593         }
23594         return false;
23595     },
23596
23597     /**
23598      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23599      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23600      * @param {Number} index The zero-based index of the list item to select
23601      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23602      * selected item if it is not currently in view (defaults to true)
23603      */
23604     select : function(index, scrollIntoView){
23605         this.selectedIndex = index;
23606         this.view.select(index);
23607         if(scrollIntoView !== false){
23608             var el = this.view.getNode(index);
23609             if(el){
23610                 this.innerList.scrollChildIntoView(el, false);
23611             }
23612         }
23613     },
23614
23615     // private
23616     selectNext : function(){
23617         var ct = this.store.getCount();
23618         if(ct > 0){
23619             if(this.selectedIndex == -1){
23620                 this.select(0);
23621             }else if(this.selectedIndex < ct-1){
23622                 this.select(this.selectedIndex+1);
23623             }
23624         }
23625     },
23626
23627     // private
23628     selectPrev : function(){
23629         var ct = this.store.getCount();
23630         if(ct > 0){
23631             if(this.selectedIndex == -1){
23632                 this.select(0);
23633             }else if(this.selectedIndex != 0){
23634                 this.select(this.selectedIndex-1);
23635             }
23636         }
23637     },
23638
23639     // private
23640     onKeyUp : function(e){
23641         if(this.editable !== false && !e.isSpecialKey()){
23642             this.lastKey = e.getKey();
23643             this.dqTask.delay(this.queryDelay);
23644         }
23645     },
23646
23647     // private
23648     validateBlur : function(){
23649         return !this.list || !this.list.isVisible();   
23650     },
23651
23652     // private
23653     initQuery : function(){
23654         this.doQuery(this.getRawValue());
23655     },
23656
23657     // private
23658     doForce : function(){
23659         if(this.el.dom.value.length > 0){
23660             this.el.dom.value =
23661                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23662              
23663         }
23664     },
23665
23666     /**
23667      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23668      * query allowing the query action to be canceled if needed.
23669      * @param {String} query The SQL query to execute
23670      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23671      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23672      * saved in the current store (defaults to false)
23673      */
23674     doQuery : function(q, forceAll){
23675         if(q === undefined || q === null){
23676             q = '';
23677         }
23678         var qe = {
23679             query: q,
23680             forceAll: forceAll,
23681             combo: this,
23682             cancel:false
23683         };
23684         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23685             return false;
23686         }
23687         q = qe.query;
23688         forceAll = qe.forceAll;
23689         if(forceAll === true || (q.length >= this.minChars)){
23690             if(this.lastQuery != q || this.alwaysQuery){
23691                 this.lastQuery = q;
23692                 if(this.mode == 'local'){
23693                     this.selectedIndex = -1;
23694                     if(forceAll){
23695                         this.store.clearFilter();
23696                     }else{
23697                         this.store.filter(this.displayField, q);
23698                     }
23699                     this.onLoad();
23700                 }else{
23701                     this.store.baseParams[this.queryParam] = q;
23702                     this.store.load({
23703                         params: this.getParams(q)
23704                     });
23705                     this.expand();
23706                 }
23707             }else{
23708                 this.selectedIndex = -1;
23709                 this.onLoad();   
23710             }
23711         }
23712     },
23713
23714     // private
23715     getParams : function(q){
23716         var p = {};
23717         //p[this.queryParam] = q;
23718         if(this.pageSize){
23719             p.start = 0;
23720             p.limit = this.pageSize;
23721         }
23722         return p;
23723     },
23724
23725     /**
23726      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23727      */
23728     collapse : function(){
23729         if(!this.isExpanded()){
23730             return;
23731         }
23732         this.list.hide();
23733         Roo.get(document).un('mousedown', this.collapseIf, this);
23734         Roo.get(document).un('mousewheel', this.collapseIf, this);
23735         if (!this.editable) {
23736             Roo.get(document).un('keydown', this.listKeyPress, this);
23737         }
23738         this.fireEvent('collapse', this);
23739     },
23740
23741     // private
23742     collapseIf : function(e){
23743         if(!e.within(this.wrap) && !e.within(this.list)){
23744             this.collapse();
23745         }
23746     },
23747
23748     /**
23749      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23750      */
23751     expand : function(){
23752         if(this.isExpanded() || !this.hasFocus){
23753             return;
23754         }
23755         this.list.alignTo(this.el, this.listAlign);
23756         this.list.show();
23757         Roo.get(document).on('mousedown', this.collapseIf, this);
23758         Roo.get(document).on('mousewheel', this.collapseIf, this);
23759         if (!this.editable) {
23760             Roo.get(document).on('keydown', this.listKeyPress, this);
23761         }
23762         
23763         this.fireEvent('expand', this);
23764     },
23765
23766     // private
23767     // Implements the default empty TriggerField.onTriggerClick function
23768     onTriggerClick : function(){
23769         if(this.disabled){
23770             return;
23771         }
23772         if(this.isExpanded()){
23773             this.collapse();
23774             if (!this.blockFocus) {
23775                 this.el.focus();
23776             }
23777             
23778         }else {
23779             this.hasFocus = true;
23780             if(this.triggerAction == 'all') {
23781                 this.doQuery(this.allQuery, true);
23782             } else {
23783                 this.doQuery(this.getRawValue());
23784             }
23785             if (!this.blockFocus) {
23786                 this.el.focus();
23787             }
23788         }
23789     },
23790     listKeyPress : function(e)
23791     {
23792         //Roo.log('listkeypress');
23793         // scroll to first matching element based on key pres..
23794         if (e.isSpecialKey()) {
23795             return false;
23796         }
23797         var k = String.fromCharCode(e.getKey()).toUpperCase();
23798         //Roo.log(k);
23799         var match  = false;
23800         var csel = this.view.getSelectedNodes();
23801         var cselitem = false;
23802         if (csel.length) {
23803             var ix = this.view.indexOf(csel[0]);
23804             cselitem  = this.store.getAt(ix);
23805             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23806                 cselitem = false;
23807             }
23808             
23809         }
23810         
23811         this.store.each(function(v) { 
23812             if (cselitem) {
23813                 // start at existing selection.
23814                 if (cselitem.id == v.id) {
23815                     cselitem = false;
23816                 }
23817                 return;
23818             }
23819                 
23820             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23821                 match = this.store.indexOf(v);
23822                 return false;
23823             }
23824         }, this);
23825         
23826         if (match === false) {
23827             return true; // no more action?
23828         }
23829         // scroll to?
23830         this.view.select(match);
23831         var sn = Roo.get(this.view.getSelectedNodes()[0])
23832         sn.scrollIntoView(sn.dom.parentNode, false);
23833     }
23834
23835     /** 
23836     * @cfg {Boolean} grow 
23837     * @hide 
23838     */
23839     /** 
23840     * @cfg {Number} growMin 
23841     * @hide 
23842     */
23843     /** 
23844     * @cfg {Number} growMax 
23845     * @hide 
23846     */
23847     /**
23848      * @hide
23849      * @method autoSize
23850      */
23851 });/*
23852  * Copyright(c) 2010-2012, Roo J Solutions Limited
23853  *
23854  * Licence LGPL
23855  *
23856  */
23857
23858 /**
23859  * @class Roo.form.ComboBoxArray
23860  * @extends Roo.form.TextField
23861  * A facebook style adder... for lists of email / people / countries  etc...
23862  * pick multiple items from a combo box, and shows each one.
23863  *
23864  *  Fred [x]  Brian [x]  [Pick another |v]
23865  *
23866  *
23867  *  For this to work: it needs various extra information
23868  *    - normal combo problay has
23869  *      name, hiddenName
23870  *    + displayField, valueField
23871  *
23872  *    For our purpose...
23873  *
23874  *
23875  *   If we change from 'extends' to wrapping...
23876  *   
23877  *  
23878  *
23879  
23880  
23881  * @constructor
23882  * Create a new ComboBoxArray.
23883  * @param {Object} config Configuration options
23884  */
23885  
23886
23887 Roo.form.ComboBoxArray = function(config)
23888 {
23889     this.addEvents({
23890         /**
23891          * @event remove
23892          * Fires when remove the value from the list
23893              * @param {Roo.form.ComboBoxArray} _self This combo box array
23894              * @param {Roo.form.ComboBoxArray.Item} item removed item
23895              */
23896         'remove' : true
23897         
23898         
23899     });
23900     
23901     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23902     
23903     this.items = new Roo.util.MixedCollection(false);
23904     
23905     // construct the child combo...
23906     
23907     
23908     
23909     
23910    
23911     
23912 }
23913
23914  
23915 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23916
23917     /**
23918      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23919      */
23920     
23921     lastData : false,
23922     
23923     // behavies liek a hiddne field
23924     inputType:      'hidden',
23925     /**
23926      * @cfg {Number} width The width of the box that displays the selected element
23927      */ 
23928     width:          300,
23929
23930     
23931     
23932     /**
23933      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23934      */
23935     name : false,
23936     /**
23937      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23938      */
23939     hiddenName : false,
23940     
23941     
23942     // private the array of items that are displayed..
23943     items  : false,
23944     // private - the hidden field el.
23945     hiddenEl : false,
23946     // private - the filed el..
23947     el : false,
23948     
23949     //validateValue : function() { return true; }, // all values are ok!
23950     //onAddClick: function() { },
23951     
23952     onRender : function(ct, position) 
23953     {
23954         
23955         // create the standard hidden element
23956         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
23957         
23958         
23959         // give fake names to child combo;
23960         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
23961         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
23962         
23963         this.combo = Roo.factory(this.combo, Roo.form);
23964         this.combo.onRender(ct, position);
23965         if (typeof(this.combo.width) != 'undefined') {
23966             this.combo.onResize(this.combo.width,0);
23967         }
23968         
23969         this.combo.initEvents();
23970         
23971         // assigned so form know we need to do this..
23972         this.store          = this.combo.store;
23973         this.valueField     = this.combo.valueField;
23974         this.displayField   = this.combo.displayField ;
23975         
23976         
23977         this.combo.wrap.addClass('x-cbarray-grp');
23978         
23979         var cbwrap = this.combo.wrap.createChild(
23980             {tag: 'div', cls: 'x-cbarray-cb'},
23981             this.combo.el.dom
23982         );
23983         
23984              
23985         this.hiddenEl = this.combo.wrap.createChild({
23986             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
23987         });
23988         this.el = this.combo.wrap.createChild({
23989             tag: 'input',  type:'hidden' , name: this.name, value : ''
23990         });
23991          //   this.el.dom.removeAttribute("name");
23992         
23993         
23994         this.outerWrap = this.combo.wrap;
23995         this.wrap = cbwrap;
23996         
23997         this.outerWrap.setWidth(this.width);
23998         this.outerWrap.dom.removeChild(this.el.dom);
23999         
24000         this.wrap.dom.appendChild(this.el.dom);
24001         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24002         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24003         
24004         this.combo.trigger.setStyle('position','relative');
24005         this.combo.trigger.setStyle('left', '0px');
24006         this.combo.trigger.setStyle('top', '2px');
24007         
24008         this.combo.el.setStyle('vertical-align', 'text-bottom');
24009         
24010         //this.trigger.setStyle('vertical-align', 'top');
24011         
24012         // this should use the code from combo really... on('add' ....)
24013         if (this.adder) {
24014             
24015         
24016             this.adder = this.outerWrap.createChild(
24017                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24018             var _t = this;
24019             this.adder.on('click', function(e) {
24020                 _t.fireEvent('adderclick', this, e);
24021             }, _t);
24022         }
24023         //var _t = this;
24024         //this.adder.on('click', this.onAddClick, _t);
24025         
24026         
24027         this.combo.on('select', function(cb, rec, ix) {
24028             this.addItem(rec.data);
24029             
24030             cb.setValue('');
24031             cb.el.dom.value = '';
24032             //cb.lastData = rec.data;
24033             // add to list
24034             
24035         }, this);
24036         
24037         
24038     },
24039     
24040     
24041     getName: function()
24042     {
24043         // returns hidden if it's set..
24044         if (!this.rendered) {return ''};
24045         return  this.hiddenName ? this.hiddenName : this.name;
24046         
24047     },
24048     
24049     
24050     onResize: function(w, h){
24051         
24052         return;
24053         // not sure if this is needed..
24054         //this.combo.onResize(w,h);
24055         
24056         if(typeof w != 'number'){
24057             // we do not handle it!?!?
24058             return;
24059         }
24060         var tw = this.combo.trigger.getWidth();
24061         tw += this.addicon ? this.addicon.getWidth() : 0;
24062         tw += this.editicon ? this.editicon.getWidth() : 0;
24063         var x = w - tw;
24064         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24065             
24066         this.combo.trigger.setStyle('left', '0px');
24067         
24068         if(this.list && this.listWidth === undefined){
24069             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24070             this.list.setWidth(lw);
24071             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24072         }
24073         
24074     
24075         
24076     },
24077     
24078     addItem: function(rec)
24079     {
24080         var valueField = this.combo.valueField;
24081         var displayField = this.combo.displayField;
24082         if (this.items.indexOfKey(rec[valueField]) > -1) {
24083             //console.log("GOT " + rec.data.id);
24084             return;
24085         }
24086         
24087         var x = new Roo.form.ComboBoxArray.Item({
24088             //id : rec[this.idField],
24089             data : rec,
24090             displayField : displayField ,
24091             tipField : displayField ,
24092             cb : this
24093         });
24094         // use the 
24095         this.items.add(rec[valueField],x);
24096         // add it before the element..
24097         this.updateHiddenEl();
24098         x.render(this.outerWrap, this.wrap.dom);
24099         // add the image handler..
24100     },
24101     
24102     updateHiddenEl : function()
24103     {
24104         this.validate();
24105         if (!this.hiddenEl) {
24106             return;
24107         }
24108         var ar = [];
24109         var idField = this.combo.valueField;
24110         
24111         this.items.each(function(f) {
24112             ar.push(f.data[idField]);
24113            
24114         });
24115         this.hiddenEl.dom.value = ar.join(',');
24116         this.validate();
24117     },
24118     
24119     reset : function()
24120     {
24121         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24122         this.items.each(function(f) {
24123            f.remove(); 
24124         });
24125         this.el.dom.value = '';
24126         if (this.hiddenEl) {
24127             this.hiddenEl.dom.value = '';
24128         }
24129         
24130     },
24131     getValue: function()
24132     {
24133         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24134     },
24135     setValue: function(v) // not a valid action - must use addItems..
24136     {
24137          
24138         this.reset();
24139         
24140         
24141         
24142         if (this.store.isLocal && (typeof(v) == 'string')) {
24143             // then we can use the store to find the values..
24144             // comma seperated at present.. this needs to allow JSON based encoding..
24145             this.hiddenEl.value  = v;
24146             var v_ar = [];
24147             Roo.each(v.split(','), function(k) {
24148                 Roo.log("CHECK " + this.valueField + ',' + k);
24149                 var li = this.store.query(this.valueField, k);
24150                 if (!li.length) {
24151                     return;
24152                 }
24153                 var add = {};
24154                 add[this.valueField] = k;
24155                 add[this.displayField] = li.item(0).data[this.displayField];
24156                 
24157                 this.addItem(add);
24158             }, this) 
24159              
24160         }
24161         if (typeof(v) == 'object' ) {
24162             // then let's assume it's an array of objects..
24163             Roo.each(v, function(l) {
24164                 this.addItem(l);
24165             }, this);
24166              
24167         }
24168         
24169         
24170     },
24171     setFromData: function(v)
24172     {
24173         // this recieves an object, if setValues is called.
24174         this.reset();
24175         this.el.dom.value = v[this.displayField];
24176         this.hiddenEl.dom.value = v[this.valueField];
24177         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24178             return;
24179         }
24180         var kv = v[this.valueField];
24181         var dv = v[this.displayField];
24182         kv = typeof(kv) != 'string' ? '' : kv;
24183         dv = typeof(dv) != 'string' ? '' : dv;
24184         
24185         
24186         var keys = kv.split(',');
24187         var display = dv.split(',');
24188         for (var i = 0 ; i < keys.length; i++) {
24189             
24190             add = {};
24191             add[this.valueField] = keys[i];
24192             add[this.displayField] = display[i];
24193             this.addItem(add);
24194         }
24195       
24196         
24197     },
24198     
24199     /**
24200      * Validates the combox array value
24201      * @return {Boolean} True if the value is valid, else false
24202      */
24203     validate : function(){
24204         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24205             this.clearInvalid();
24206             return true;
24207         }
24208         return false;
24209     },
24210     
24211     validateValue : function(value){
24212         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24213         
24214     },
24215     
24216     /*@
24217      * overide
24218      * 
24219      */
24220     isDirty : function() {
24221         if(this.disabled) {
24222             return false;
24223         }
24224         
24225         try {
24226             var d = Roo.decode(String(this.originalValue));
24227         } catch (e) {
24228             return String(this.getValue()) !== String(this.originalValue);
24229         }
24230         
24231         var originalValue = [];
24232         
24233         for (var i = 0; i < d.length; i++){
24234             originalValue.push(d[i][this.valueField]);
24235         }
24236         
24237         return String(this.getValue()) !== String(originalValue.join(','));
24238         
24239     }
24240     
24241 });
24242
24243
24244
24245 /**
24246  * @class Roo.form.ComboBoxArray.Item
24247  * @extends Roo.BoxComponent
24248  * A selected item in the list
24249  *  Fred [x]  Brian [x]  [Pick another |v]
24250  * 
24251  * @constructor
24252  * Create a new item.
24253  * @param {Object} config Configuration options
24254  */
24255  
24256 Roo.form.ComboBoxArray.Item = function(config) {
24257     config.id = Roo.id();
24258     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24259 }
24260
24261 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24262     data : {},
24263     cb: false,
24264     displayField : false,
24265     tipField : false,
24266     
24267     
24268     defaultAutoCreate : {
24269         tag: 'div',
24270         cls: 'x-cbarray-item',
24271         cn : [ 
24272             { tag: 'div' },
24273             {
24274                 tag: 'img',
24275                 width:16,
24276                 height : 16,
24277                 src : Roo.BLANK_IMAGE_URL ,
24278                 align: 'center'
24279             }
24280         ]
24281         
24282     },
24283     
24284  
24285     onRender : function(ct, position)
24286     {
24287         Roo.form.Field.superclass.onRender.call(this, ct, position);
24288         
24289         if(!this.el){
24290             var cfg = this.getAutoCreate();
24291             this.el = ct.createChild(cfg, position);
24292         }
24293         
24294         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24295         
24296         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24297             this.cb.renderer(this.data) :
24298             String.format('{0}',this.data[this.displayField]);
24299         
24300             
24301         this.el.child('div').dom.setAttribute('qtip',
24302                         String.format('{0}',this.data[this.tipField])
24303         );
24304         
24305         this.el.child('img').on('click', this.remove, this);
24306         
24307     },
24308    
24309     remove : function()
24310     {
24311         if(this.cb.disabled){
24312             return;
24313         }
24314         this.cb.items.remove(this);
24315         this.el.child('img').un('click', this.remove, this);
24316         this.el.remove();
24317         this.cb.updateHiddenEl();
24318         
24319         this.cb.fireEvent('remove', this.cb, this);
24320     }
24321 });/*
24322  * Based on:
24323  * Ext JS Library 1.1.1
24324  * Copyright(c) 2006-2007, Ext JS, LLC.
24325  *
24326  * Originally Released Under LGPL - original licence link has changed is not relivant.
24327  *
24328  * Fork - LGPL
24329  * <script type="text/javascript">
24330  */
24331 /**
24332  * @class Roo.form.Checkbox
24333  * @extends Roo.form.Field
24334  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24335  * @constructor
24336  * Creates a new Checkbox
24337  * @param {Object} config Configuration options
24338  */
24339 Roo.form.Checkbox = function(config){
24340     Roo.form.Checkbox.superclass.constructor.call(this, config);
24341     this.addEvents({
24342         /**
24343          * @event check
24344          * Fires when the checkbox is checked or unchecked.
24345              * @param {Roo.form.Checkbox} this This checkbox
24346              * @param {Boolean} checked The new checked value
24347              */
24348         check : true
24349     });
24350 };
24351
24352 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24353     /**
24354      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24355      */
24356     focusClass : undefined,
24357     /**
24358      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24359      */
24360     fieldClass: "x-form-field",
24361     /**
24362      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24363      */
24364     checked: false,
24365     /**
24366      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24367      * {tag: "input", type: "checkbox", autocomplete: "off"})
24368      */
24369     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24370     /**
24371      * @cfg {String} boxLabel The text that appears beside the checkbox
24372      */
24373     boxLabel : "",
24374     /**
24375      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24376      */  
24377     inputValue : '1',
24378     /**
24379      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24380      */
24381      valueOff: '0', // value when not checked..
24382
24383     actionMode : 'viewEl', 
24384     //
24385     // private
24386     itemCls : 'x-menu-check-item x-form-item',
24387     groupClass : 'x-menu-group-item',
24388     inputType : 'hidden',
24389     
24390     
24391     inSetChecked: false, // check that we are not calling self...
24392     
24393     inputElement: false, // real input element?
24394     basedOn: false, // ????
24395     
24396     isFormField: true, // not sure where this is needed!!!!
24397
24398     onResize : function(){
24399         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24400         if(!this.boxLabel){
24401             this.el.alignTo(this.wrap, 'c-c');
24402         }
24403     },
24404
24405     initEvents : function(){
24406         Roo.form.Checkbox.superclass.initEvents.call(this);
24407         this.el.on("click", this.onClick,  this);
24408         this.el.on("change", this.onClick,  this);
24409     },
24410
24411
24412     getResizeEl : function(){
24413         return this.wrap;
24414     },
24415
24416     getPositionEl : function(){
24417         return this.wrap;
24418     },
24419
24420     // private
24421     onRender : function(ct, position){
24422         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24423         /*
24424         if(this.inputValue !== undefined){
24425             this.el.dom.value = this.inputValue;
24426         }
24427         */
24428         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24429         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24430         var viewEl = this.wrap.createChild({ 
24431             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24432         this.viewEl = viewEl;   
24433         this.wrap.on('click', this.onClick,  this); 
24434         
24435         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24436         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24437         
24438         
24439         
24440         if(this.boxLabel){
24441             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24442         //    viewEl.on('click', this.onClick,  this); 
24443         }
24444         //if(this.checked){
24445             this.setChecked(this.checked);
24446         //}else{
24447             //this.checked = this.el.dom;
24448         //}
24449
24450     },
24451
24452     // private
24453     initValue : Roo.emptyFn,
24454
24455     /**
24456      * Returns the checked state of the checkbox.
24457      * @return {Boolean} True if checked, else false
24458      */
24459     getValue : function(){
24460         if(this.el){
24461             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24462         }
24463         return this.valueOff;
24464         
24465     },
24466
24467         // private
24468     onClick : function(){ 
24469         if (this.disabled) {
24470             return;
24471         }
24472         this.setChecked(!this.checked);
24473
24474         //if(this.el.dom.checked != this.checked){
24475         //    this.setValue(this.el.dom.checked);
24476        // }
24477     },
24478
24479     /**
24480      * Sets the checked state of the checkbox.
24481      * On is always based on a string comparison between inputValue and the param.
24482      * @param {Boolean/String} value - the value to set 
24483      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24484      */
24485     setValue : function(v,suppressEvent){
24486         
24487         
24488         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24489         //if(this.el && this.el.dom){
24490         //    this.el.dom.checked = this.checked;
24491         //    this.el.dom.defaultChecked = this.checked;
24492         //}
24493         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24494         //this.fireEvent("check", this, this.checked);
24495     },
24496     // private..
24497     setChecked : function(state,suppressEvent)
24498     {
24499         if (this.inSetChecked) {
24500             this.checked = state;
24501             return;
24502         }
24503         
24504     
24505         if(this.wrap){
24506             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24507         }
24508         this.checked = state;
24509         if(suppressEvent !== true){
24510             this.fireEvent('check', this, state);
24511         }
24512         this.inSetChecked = true;
24513         this.el.dom.value = state ? this.inputValue : this.valueOff;
24514         this.inSetChecked = false;
24515         
24516     },
24517     // handle setting of hidden value by some other method!!?!?
24518     setFromHidden: function()
24519     {
24520         if(!this.el){
24521             return;
24522         }
24523         //console.log("SET FROM HIDDEN");
24524         //alert('setFrom hidden');
24525         this.setValue(this.el.dom.value);
24526     },
24527     
24528     onDestroy : function()
24529     {
24530         if(this.viewEl){
24531             Roo.get(this.viewEl).remove();
24532         }
24533          
24534         Roo.form.Checkbox.superclass.onDestroy.call(this);
24535     }
24536
24537 });/*
24538  * Based on:
24539  * Ext JS Library 1.1.1
24540  * Copyright(c) 2006-2007, Ext JS, LLC.
24541  *
24542  * Originally Released Under LGPL - original licence link has changed is not relivant.
24543  *
24544  * Fork - LGPL
24545  * <script type="text/javascript">
24546  */
24547  
24548 /**
24549  * @class Roo.form.Radio
24550  * @extends Roo.form.Checkbox
24551  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24552  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24553  * @constructor
24554  * Creates a new Radio
24555  * @param {Object} config Configuration options
24556  */
24557 Roo.form.Radio = function(){
24558     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24559 };
24560 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24561     inputType: 'radio',
24562
24563     /**
24564      * If this radio is part of a group, it will return the selected value
24565      * @return {String}
24566      */
24567     getGroupValue : function(){
24568         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24569     },
24570     
24571     
24572     onRender : function(ct, position){
24573         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24574         
24575         if(this.inputValue !== undefined){
24576             this.el.dom.value = this.inputValue;
24577         }
24578          
24579         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24580         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24581         //var viewEl = this.wrap.createChild({ 
24582         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24583         //this.viewEl = viewEl;   
24584         //this.wrap.on('click', this.onClick,  this); 
24585         
24586         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24587         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24588         
24589         
24590         
24591         if(this.boxLabel){
24592             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24593         //    viewEl.on('click', this.onClick,  this); 
24594         }
24595          if(this.checked){
24596             this.el.dom.checked =   'checked' ;
24597         }
24598          
24599     } 
24600     
24601     
24602 });//<script type="text/javascript">
24603
24604 /*
24605  * Based  Ext JS Library 1.1.1
24606  * Copyright(c) 2006-2007, Ext JS, LLC.
24607  * LGPL
24608  *
24609  */
24610  
24611 /**
24612  * @class Roo.HtmlEditorCore
24613  * @extends Roo.Component
24614  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24615  *
24616  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24617  */
24618
24619 Roo.HtmlEditorCore = function(config){
24620     
24621     
24622     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24623     
24624     
24625     this.addEvents({
24626         /**
24627          * @event initialize
24628          * Fires when the editor is fully initialized (including the iframe)
24629          * @param {Roo.HtmlEditorCore} this
24630          */
24631         initialize: true,
24632         /**
24633          * @event activate
24634          * Fires when the editor is first receives the focus. Any insertion must wait
24635          * until after this event.
24636          * @param {Roo.HtmlEditorCore} this
24637          */
24638         activate: true,
24639          /**
24640          * @event beforesync
24641          * Fires before the textarea is updated with content from the editor iframe. Return false
24642          * to cancel the sync.
24643          * @param {Roo.HtmlEditorCore} this
24644          * @param {String} html
24645          */
24646         beforesync: true,
24647          /**
24648          * @event beforepush
24649          * Fires before the iframe editor is updated with content from the textarea. Return false
24650          * to cancel the push.
24651          * @param {Roo.HtmlEditorCore} this
24652          * @param {String} html
24653          */
24654         beforepush: true,
24655          /**
24656          * @event sync
24657          * Fires when the textarea is updated with content from the editor iframe.
24658          * @param {Roo.HtmlEditorCore} this
24659          * @param {String} html
24660          */
24661         sync: true,
24662          /**
24663          * @event push
24664          * Fires when the iframe editor is updated with content from the textarea.
24665          * @param {Roo.HtmlEditorCore} this
24666          * @param {String} html
24667          */
24668         push: true,
24669         
24670         /**
24671          * @event editorevent
24672          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24673          * @param {Roo.HtmlEditorCore} this
24674          */
24675         editorevent: true
24676     });
24677     
24678     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24679     
24680     // defaults : white / black...
24681     this.applyBlacklists();
24682     
24683     
24684     
24685 };
24686
24687
24688 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24689
24690
24691      /**
24692      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24693      */
24694     
24695     owner : false,
24696     
24697      /**
24698      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24699      *                        Roo.resizable.
24700      */
24701     resizable : false,
24702      /**
24703      * @cfg {Number} height (in pixels)
24704      */   
24705     height: 300,
24706    /**
24707      * @cfg {Number} width (in pixels)
24708      */   
24709     width: 500,
24710     
24711     /**
24712      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24713      * 
24714      */
24715     stylesheets: false,
24716     
24717     // id of frame..
24718     frameId: false,
24719     
24720     // private properties
24721     validationEvent : false,
24722     deferHeight: true,
24723     initialized : false,
24724     activated : false,
24725     sourceEditMode : false,
24726     onFocus : Roo.emptyFn,
24727     iframePad:3,
24728     hideMode:'offsets',
24729     
24730     clearUp: true,
24731     
24732     // blacklist + whitelisted elements..
24733     black: false,
24734     white: false,
24735      
24736     
24737
24738     /**
24739      * Protected method that will not generally be called directly. It
24740      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24741      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24742      */
24743     getDocMarkup : function(){
24744         // body styles..
24745         var st = '';
24746         Roo.log(this.stylesheets);
24747         
24748         // inherit styels from page...?? 
24749         if (this.stylesheets === false) {
24750             
24751             Roo.get(document.head).select('style').each(function(node) {
24752                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24753             });
24754             
24755             Roo.get(document.head).select('link').each(function(node) { 
24756                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24757             });
24758             
24759         } else if (!this.stylesheets.length) {
24760                 // simple..
24761                 st = '<style type="text/css">' +
24762                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24763                    '</style>';
24764         } else {
24765             Roo.each(this.stylesheets, function(s) {
24766                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24767             });
24768             
24769         }
24770         
24771         st +=  '<style type="text/css">' +
24772             'IMG { cursor: pointer } ' +
24773         '</style>';
24774
24775         
24776         return '<html><head>' + st  +
24777             //<style type="text/css">' +
24778             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24779             //'</style>' +
24780             ' </head><body class="roo-htmleditor-body"></body></html>';
24781     },
24782
24783     // private
24784     onRender : function(ct, position)
24785     {
24786         var _t = this;
24787         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24788         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24789         
24790         
24791         this.el.dom.style.border = '0 none';
24792         this.el.dom.setAttribute('tabIndex', -1);
24793         this.el.addClass('x-hidden hide');
24794         
24795         
24796         
24797         if(Roo.isIE){ // fix IE 1px bogus margin
24798             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24799         }
24800        
24801         
24802         this.frameId = Roo.id();
24803         
24804          
24805         
24806         var iframe = this.owner.wrap.createChild({
24807             tag: 'iframe',
24808             cls: 'form-control', // bootstrap..
24809             id: this.frameId,
24810             name: this.frameId,
24811             frameBorder : 'no',
24812             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24813         }, this.el
24814         );
24815         
24816         
24817         this.iframe = iframe.dom;
24818
24819          this.assignDocWin();
24820         
24821         this.doc.designMode = 'on';
24822        
24823         this.doc.open();
24824         this.doc.write(this.getDocMarkup());
24825         this.doc.close();
24826
24827         
24828         var task = { // must defer to wait for browser to be ready
24829             run : function(){
24830                 //console.log("run task?" + this.doc.readyState);
24831                 this.assignDocWin();
24832                 if(this.doc.body || this.doc.readyState == 'complete'){
24833                     try {
24834                         this.doc.designMode="on";
24835                     } catch (e) {
24836                         return;
24837                     }
24838                     Roo.TaskMgr.stop(task);
24839                     this.initEditor.defer(10, this);
24840                 }
24841             },
24842             interval : 10,
24843             duration: 10000,
24844             scope: this
24845         };
24846         Roo.TaskMgr.start(task);
24847
24848         
24849          
24850     },
24851
24852     // private
24853     onResize : function(w, h)
24854     {
24855          Roo.log('resize: ' +w + ',' + h );
24856         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24857         if(!this.iframe){
24858             return;
24859         }
24860         if(typeof w == 'number'){
24861             
24862             this.iframe.style.width = w + 'px';
24863         }
24864         if(typeof h == 'number'){
24865             
24866             this.iframe.style.height = h + 'px';
24867             if(this.doc){
24868                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24869             }
24870         }
24871         
24872     },
24873
24874     /**
24875      * Toggles the editor between standard and source edit mode.
24876      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24877      */
24878     toggleSourceEdit : function(sourceEditMode){
24879         
24880         this.sourceEditMode = sourceEditMode === true;
24881         
24882         if(this.sourceEditMode){
24883  
24884             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24885             
24886         }else{
24887             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24888             //this.iframe.className = '';
24889             this.deferFocus();
24890         }
24891         //this.setSize(this.owner.wrap.getSize());
24892         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24893     },
24894
24895     
24896   
24897
24898     /**
24899      * Protected method that will not generally be called directly. If you need/want
24900      * custom HTML cleanup, this is the method you should override.
24901      * @param {String} html The HTML to be cleaned
24902      * return {String} The cleaned HTML
24903      */
24904     cleanHtml : function(html){
24905         html = String(html);
24906         if(html.length > 5){
24907             if(Roo.isSafari){ // strip safari nonsense
24908                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24909             }
24910         }
24911         if(html == '&nbsp;'){
24912             html = '';
24913         }
24914         return html;
24915     },
24916
24917     /**
24918      * HTML Editor -> Textarea
24919      * Protected method that will not generally be called directly. Syncs the contents
24920      * of the editor iframe with the textarea.
24921      */
24922     syncValue : function(){
24923         if(this.initialized){
24924             var bd = (this.doc.body || this.doc.documentElement);
24925             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24926             var html = bd.innerHTML;
24927             if(Roo.isSafari){
24928                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24929                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24930                 if(m && m[1]){
24931                     html = '<div style="'+m[0]+'">' + html + '</div>';
24932                 }
24933             }
24934             html = this.cleanHtml(html);
24935             // fix up the special chars.. normaly like back quotes in word...
24936             // however we do not want to do this with chinese..
24937             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24938                 var cc = b.charCodeAt();
24939                 if (
24940                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24941                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24942                     (cc >= 0xf900 && cc < 0xfb00 )
24943                 ) {
24944                         return b;
24945                 }
24946                 return "&#"+cc+";" 
24947             });
24948             if(this.owner.fireEvent('beforesync', this, html) !== false){
24949                 this.el.dom.value = html;
24950                 this.owner.fireEvent('sync', this, html);
24951             }
24952         }
24953     },
24954
24955     /**
24956      * Protected method that will not generally be called directly. Pushes the value of the textarea
24957      * into the iframe editor.
24958      */
24959     pushValue : function(){
24960         if(this.initialized){
24961             var v = this.el.dom.value.trim();
24962             
24963 //            if(v.length < 1){
24964 //                v = '&#160;';
24965 //            }
24966             
24967             if(this.owner.fireEvent('beforepush', this, v) !== false){
24968                 var d = (this.doc.body || this.doc.documentElement);
24969                 d.innerHTML = v;
24970                 this.cleanUpPaste();
24971                 this.el.dom.value = d.innerHTML;
24972                 this.owner.fireEvent('push', this, v);
24973             }
24974         }
24975     },
24976
24977     // private
24978     deferFocus : function(){
24979         this.focus.defer(10, this);
24980     },
24981
24982     // doc'ed in Field
24983     focus : function(){
24984         if(this.win && !this.sourceEditMode){
24985             this.win.focus();
24986         }else{
24987             this.el.focus();
24988         }
24989     },
24990     
24991     assignDocWin: function()
24992     {
24993         var iframe = this.iframe;
24994         
24995          if(Roo.isIE){
24996             this.doc = iframe.contentWindow.document;
24997             this.win = iframe.contentWindow;
24998         } else {
24999 //            if (!Roo.get(this.frameId)) {
25000 //                return;
25001 //            }
25002 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25003 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25004             
25005             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25006                 return;
25007             }
25008             
25009             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25010             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25011         }
25012     },
25013     
25014     // private
25015     initEditor : function(){
25016         //console.log("INIT EDITOR");
25017         this.assignDocWin();
25018         
25019         
25020         
25021         this.doc.designMode="on";
25022         this.doc.open();
25023         this.doc.write(this.getDocMarkup());
25024         this.doc.close();
25025         
25026         var dbody = (this.doc.body || this.doc.documentElement);
25027         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25028         // this copies styles from the containing element into thsi one..
25029         // not sure why we need all of this..
25030         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25031         
25032         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25033         //ss['background-attachment'] = 'fixed'; // w3c
25034         dbody.bgProperties = 'fixed'; // ie
25035         //Roo.DomHelper.applyStyles(dbody, ss);
25036         Roo.EventManager.on(this.doc, {
25037             //'mousedown': this.onEditorEvent,
25038             'mouseup': this.onEditorEvent,
25039             'dblclick': this.onEditorEvent,
25040             'click': this.onEditorEvent,
25041             'keyup': this.onEditorEvent,
25042             buffer:100,
25043             scope: this
25044         });
25045         if(Roo.isGecko){
25046             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25047         }
25048         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25049             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25050         }
25051         this.initialized = true;
25052
25053         this.owner.fireEvent('initialize', this);
25054         this.pushValue();
25055     },
25056
25057     // private
25058     onDestroy : function(){
25059         
25060         
25061         
25062         if(this.rendered){
25063             
25064             //for (var i =0; i < this.toolbars.length;i++) {
25065             //    // fixme - ask toolbars for heights?
25066             //    this.toolbars[i].onDestroy();
25067            // }
25068             
25069             //this.wrap.dom.innerHTML = '';
25070             //this.wrap.remove();
25071         }
25072     },
25073
25074     // private
25075     onFirstFocus : function(){
25076         
25077         this.assignDocWin();
25078         
25079         
25080         this.activated = true;
25081          
25082     
25083         if(Roo.isGecko){ // prevent silly gecko errors
25084             this.win.focus();
25085             var s = this.win.getSelection();
25086             if(!s.focusNode || s.focusNode.nodeType != 3){
25087                 var r = s.getRangeAt(0);
25088                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25089                 r.collapse(true);
25090                 this.deferFocus();
25091             }
25092             try{
25093                 this.execCmd('useCSS', true);
25094                 this.execCmd('styleWithCSS', false);
25095             }catch(e){}
25096         }
25097         this.owner.fireEvent('activate', this);
25098     },
25099
25100     // private
25101     adjustFont: function(btn){
25102         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25103         //if(Roo.isSafari){ // safari
25104         //    adjust *= 2;
25105        // }
25106         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25107         if(Roo.isSafari){ // safari
25108             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25109             v =  (v < 10) ? 10 : v;
25110             v =  (v > 48) ? 48 : v;
25111             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25112             
25113         }
25114         
25115         
25116         v = Math.max(1, v+adjust);
25117         
25118         this.execCmd('FontSize', v  );
25119     },
25120
25121     onEditorEvent : function(e){
25122         this.owner.fireEvent('editorevent', this, e);
25123       //  this.updateToolbar();
25124         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25125     },
25126
25127     insertTag : function(tg)
25128     {
25129         // could be a bit smarter... -> wrap the current selected tRoo..
25130         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25131             
25132             range = this.createRange(this.getSelection());
25133             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25134             wrappingNode.appendChild(range.extractContents());
25135             range.insertNode(wrappingNode);
25136
25137             return;
25138             
25139             
25140             
25141         }
25142         this.execCmd("formatblock",   tg);
25143         
25144     },
25145     
25146     insertText : function(txt)
25147     {
25148         
25149         
25150         var range = this.createRange();
25151         range.deleteContents();
25152                //alert(Sender.getAttribute('label'));
25153                
25154         range.insertNode(this.doc.createTextNode(txt));
25155     } ,
25156     
25157      
25158
25159     /**
25160      * Executes a Midas editor command on the editor document and performs necessary focus and
25161      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25162      * @param {String} cmd The Midas command
25163      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25164      */
25165     relayCmd : function(cmd, value){
25166         this.win.focus();
25167         this.execCmd(cmd, value);
25168         this.owner.fireEvent('editorevent', this);
25169         //this.updateToolbar();
25170         this.owner.deferFocus();
25171     },
25172
25173     /**
25174      * Executes a Midas editor command directly on the editor document.
25175      * For visual commands, you should use {@link #relayCmd} instead.
25176      * <b>This should only be called after the editor is initialized.</b>
25177      * @param {String} cmd The Midas command
25178      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25179      */
25180     execCmd : function(cmd, value){
25181         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25182         this.syncValue();
25183     },
25184  
25185  
25186    
25187     /**
25188      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25189      * to insert tRoo.
25190      * @param {String} text | dom node.. 
25191      */
25192     insertAtCursor : function(text)
25193     {
25194         
25195         
25196         
25197         if(!this.activated){
25198             return;
25199         }
25200         /*
25201         if(Roo.isIE){
25202             this.win.focus();
25203             var r = this.doc.selection.createRange();
25204             if(r){
25205                 r.collapse(true);
25206                 r.pasteHTML(text);
25207                 this.syncValue();
25208                 this.deferFocus();
25209             
25210             }
25211             return;
25212         }
25213         */
25214         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25215             this.win.focus();
25216             
25217             
25218             // from jquery ui (MIT licenced)
25219             var range, node;
25220             var win = this.win;
25221             
25222             if (win.getSelection && win.getSelection().getRangeAt) {
25223                 range = win.getSelection().getRangeAt(0);
25224                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25225                 range.insertNode(node);
25226             } else if (win.document.selection && win.document.selection.createRange) {
25227                 // no firefox support
25228                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25229                 win.document.selection.createRange().pasteHTML(txt);
25230             } else {
25231                 // no firefox support
25232                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25233                 this.execCmd('InsertHTML', txt);
25234             } 
25235             
25236             this.syncValue();
25237             
25238             this.deferFocus();
25239         }
25240     },
25241  // private
25242     mozKeyPress : function(e){
25243         if(e.ctrlKey){
25244             var c = e.getCharCode(), cmd;
25245           
25246             if(c > 0){
25247                 c = String.fromCharCode(c).toLowerCase();
25248                 switch(c){
25249                     case 'b':
25250                         cmd = 'bold';
25251                         break;
25252                     case 'i':
25253                         cmd = 'italic';
25254                         break;
25255                     
25256                     case 'u':
25257                         cmd = 'underline';
25258                         break;
25259                     
25260                     case 'v':
25261                         this.cleanUpPaste.defer(100, this);
25262                         return;
25263                         
25264                 }
25265                 if(cmd){
25266                     this.win.focus();
25267                     this.execCmd(cmd);
25268                     this.deferFocus();
25269                     e.preventDefault();
25270                 }
25271                 
25272             }
25273         }
25274     },
25275
25276     // private
25277     fixKeys : function(){ // load time branching for fastest keydown performance
25278         if(Roo.isIE){
25279             return function(e){
25280                 var k = e.getKey(), r;
25281                 if(k == e.TAB){
25282                     e.stopEvent();
25283                     r = this.doc.selection.createRange();
25284                     if(r){
25285                         r.collapse(true);
25286                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25287                         this.deferFocus();
25288                     }
25289                     return;
25290                 }
25291                 
25292                 if(k == e.ENTER){
25293                     r = this.doc.selection.createRange();
25294                     if(r){
25295                         var target = r.parentElement();
25296                         if(!target || target.tagName.toLowerCase() != 'li'){
25297                             e.stopEvent();
25298                             r.pasteHTML('<br />');
25299                             r.collapse(false);
25300                             r.select();
25301                         }
25302                     }
25303                 }
25304                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25305                     this.cleanUpPaste.defer(100, this);
25306                     return;
25307                 }
25308                 
25309                 
25310             };
25311         }else if(Roo.isOpera){
25312             return function(e){
25313                 var k = e.getKey();
25314                 if(k == e.TAB){
25315                     e.stopEvent();
25316                     this.win.focus();
25317                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25318                     this.deferFocus();
25319                 }
25320                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25321                     this.cleanUpPaste.defer(100, this);
25322                     return;
25323                 }
25324                 
25325             };
25326         }else if(Roo.isSafari){
25327             return function(e){
25328                 var k = e.getKey();
25329                 
25330                 if(k == e.TAB){
25331                     e.stopEvent();
25332                     this.execCmd('InsertText','\t');
25333                     this.deferFocus();
25334                     return;
25335                 }
25336                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25337                     this.cleanUpPaste.defer(100, this);
25338                     return;
25339                 }
25340                 
25341              };
25342         }
25343     }(),
25344     
25345     getAllAncestors: function()
25346     {
25347         var p = this.getSelectedNode();
25348         var a = [];
25349         if (!p) {
25350             a.push(p); // push blank onto stack..
25351             p = this.getParentElement();
25352         }
25353         
25354         
25355         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25356             a.push(p);
25357             p = p.parentNode;
25358         }
25359         a.push(this.doc.body);
25360         return a;
25361     },
25362     lastSel : false,
25363     lastSelNode : false,
25364     
25365     
25366     getSelection : function() 
25367     {
25368         this.assignDocWin();
25369         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25370     },
25371     
25372     getSelectedNode: function() 
25373     {
25374         // this may only work on Gecko!!!
25375         
25376         // should we cache this!!!!
25377         
25378         
25379         
25380          
25381         var range = this.createRange(this.getSelection()).cloneRange();
25382         
25383         if (Roo.isIE) {
25384             var parent = range.parentElement();
25385             while (true) {
25386                 var testRange = range.duplicate();
25387                 testRange.moveToElementText(parent);
25388                 if (testRange.inRange(range)) {
25389                     break;
25390                 }
25391                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25392                     break;
25393                 }
25394                 parent = parent.parentElement;
25395             }
25396             return parent;
25397         }
25398         
25399         // is ancestor a text element.
25400         var ac =  range.commonAncestorContainer;
25401         if (ac.nodeType == 3) {
25402             ac = ac.parentNode;
25403         }
25404         
25405         var ar = ac.childNodes;
25406          
25407         var nodes = [];
25408         var other_nodes = [];
25409         var has_other_nodes = false;
25410         for (var i=0;i<ar.length;i++) {
25411             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25412                 continue;
25413             }
25414             // fullly contained node.
25415             
25416             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25417                 nodes.push(ar[i]);
25418                 continue;
25419             }
25420             
25421             // probably selected..
25422             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25423                 other_nodes.push(ar[i]);
25424                 continue;
25425             }
25426             // outer..
25427             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25428                 continue;
25429             }
25430             
25431             
25432             has_other_nodes = true;
25433         }
25434         if (!nodes.length && other_nodes.length) {
25435             nodes= other_nodes;
25436         }
25437         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25438             return false;
25439         }
25440         
25441         return nodes[0];
25442     },
25443     createRange: function(sel)
25444     {
25445         // this has strange effects when using with 
25446         // top toolbar - not sure if it's a great idea.
25447         //this.editor.contentWindow.focus();
25448         if (typeof sel != "undefined") {
25449             try {
25450                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25451             } catch(e) {
25452                 return this.doc.createRange();
25453             }
25454         } else {
25455             return this.doc.createRange();
25456         }
25457     },
25458     getParentElement: function()
25459     {
25460         
25461         this.assignDocWin();
25462         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25463         
25464         var range = this.createRange(sel);
25465          
25466         try {
25467             var p = range.commonAncestorContainer;
25468             while (p.nodeType == 3) { // text node
25469                 p = p.parentNode;
25470             }
25471             return p;
25472         } catch (e) {
25473             return null;
25474         }
25475     
25476     },
25477     /***
25478      *
25479      * Range intersection.. the hard stuff...
25480      *  '-1' = before
25481      *  '0' = hits..
25482      *  '1' = after.
25483      *         [ -- selected range --- ]
25484      *   [fail]                        [fail]
25485      *
25486      *    basically..
25487      *      if end is before start or  hits it. fail.
25488      *      if start is after end or hits it fail.
25489      *
25490      *   if either hits (but other is outside. - then it's not 
25491      *   
25492      *    
25493      **/
25494     
25495     
25496     // @see http://www.thismuchiknow.co.uk/?p=64.
25497     rangeIntersectsNode : function(range, node)
25498     {
25499         var nodeRange = node.ownerDocument.createRange();
25500         try {
25501             nodeRange.selectNode(node);
25502         } catch (e) {
25503             nodeRange.selectNodeContents(node);
25504         }
25505     
25506         var rangeStartRange = range.cloneRange();
25507         rangeStartRange.collapse(true);
25508     
25509         var rangeEndRange = range.cloneRange();
25510         rangeEndRange.collapse(false);
25511     
25512         var nodeStartRange = nodeRange.cloneRange();
25513         nodeStartRange.collapse(true);
25514     
25515         var nodeEndRange = nodeRange.cloneRange();
25516         nodeEndRange.collapse(false);
25517     
25518         return rangeStartRange.compareBoundaryPoints(
25519                  Range.START_TO_START, nodeEndRange) == -1 &&
25520                rangeEndRange.compareBoundaryPoints(
25521                  Range.START_TO_START, nodeStartRange) == 1;
25522         
25523          
25524     },
25525     rangeCompareNode : function(range, node)
25526     {
25527         var nodeRange = node.ownerDocument.createRange();
25528         try {
25529             nodeRange.selectNode(node);
25530         } catch (e) {
25531             nodeRange.selectNodeContents(node);
25532         }
25533         
25534         
25535         range.collapse(true);
25536     
25537         nodeRange.collapse(true);
25538      
25539         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25540         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25541          
25542         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25543         
25544         var nodeIsBefore   =  ss == 1;
25545         var nodeIsAfter    = ee == -1;
25546         
25547         if (nodeIsBefore && nodeIsAfter)
25548             return 0; // outer
25549         if (!nodeIsBefore && nodeIsAfter)
25550             return 1; //right trailed.
25551         
25552         if (nodeIsBefore && !nodeIsAfter)
25553             return 2;  // left trailed.
25554         // fully contined.
25555         return 3;
25556     },
25557
25558     // private? - in a new class?
25559     cleanUpPaste :  function()
25560     {
25561         // cleans up the whole document..
25562         Roo.log('cleanuppaste');
25563         
25564         this.cleanUpChildren(this.doc.body);
25565         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25566         if (clean != this.doc.body.innerHTML) {
25567             this.doc.body.innerHTML = clean;
25568         }
25569         
25570     },
25571     
25572     cleanWordChars : function(input) {// change the chars to hex code
25573         var he = Roo.HtmlEditorCore;
25574         
25575         var output = input;
25576         Roo.each(he.swapCodes, function(sw) { 
25577             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25578             
25579             output = output.replace(swapper, sw[1]);
25580         });
25581         
25582         return output;
25583     },
25584     
25585     
25586     cleanUpChildren : function (n)
25587     {
25588         if (!n.childNodes.length) {
25589             return;
25590         }
25591         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25592            this.cleanUpChild(n.childNodes[i]);
25593         }
25594     },
25595     
25596     
25597         
25598     
25599     cleanUpChild : function (node)
25600     {
25601         var ed = this;
25602         //console.log(node);
25603         if (node.nodeName == "#text") {
25604             // clean up silly Windows -- stuff?
25605             return; 
25606         }
25607         if (node.nodeName == "#comment") {
25608             node.parentNode.removeChild(node);
25609             // clean up silly Windows -- stuff?
25610             return; 
25611         }
25612         var lcname = node.tagName.toLowerCase();
25613         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25614         // whitelist of tags..
25615         
25616         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25617             // remove node.
25618             node.parentNode.removeChild(node);
25619             return;
25620             
25621         }
25622         
25623         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25624         
25625         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25626         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25627         
25628         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25629         //    remove_keep_children = true;
25630         //}
25631         
25632         if (remove_keep_children) {
25633             this.cleanUpChildren(node);
25634             // inserts everything just before this node...
25635             while (node.childNodes.length) {
25636                 var cn = node.childNodes[0];
25637                 node.removeChild(cn);
25638                 node.parentNode.insertBefore(cn, node);
25639             }
25640             node.parentNode.removeChild(node);
25641             return;
25642         }
25643         
25644         if (!node.attributes || !node.attributes.length) {
25645             this.cleanUpChildren(node);
25646             return;
25647         }
25648         
25649         function cleanAttr(n,v)
25650         {
25651             
25652             if (v.match(/^\./) || v.match(/^\//)) {
25653                 return;
25654             }
25655             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25656                 return;
25657             }
25658             if (v.match(/^#/)) {
25659                 return;
25660             }
25661 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25662             node.removeAttribute(n);
25663             
25664         }
25665         
25666         var cwhite = this.cwhite;
25667         var cblack = this.cblack;
25668             
25669         function cleanStyle(n,v)
25670         {
25671             if (v.match(/expression/)) { //XSS?? should we even bother..
25672                 node.removeAttribute(n);
25673                 return;
25674             }
25675             
25676             var parts = v.split(/;/);
25677             var clean = [];
25678             
25679             Roo.each(parts, function(p) {
25680                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25681                 if (!p.length) {
25682                     return true;
25683                 }
25684                 var l = p.split(':').shift().replace(/\s+/g,'');
25685                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25686                 
25687                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25688 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25689                     //node.removeAttribute(n);
25690                     return true;
25691                 }
25692                 //Roo.log()
25693                 // only allow 'c whitelisted system attributes'
25694                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25695 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25696                     //node.removeAttribute(n);
25697                     return true;
25698                 }
25699                 
25700                 
25701                  
25702                 
25703                 clean.push(p);
25704                 return true;
25705             });
25706             if (clean.length) { 
25707                 node.setAttribute(n, clean.join(';'));
25708             } else {
25709                 node.removeAttribute(n);
25710             }
25711             
25712         }
25713         
25714         
25715         for (var i = node.attributes.length-1; i > -1 ; i--) {
25716             var a = node.attributes[i];
25717             //console.log(a);
25718             
25719             if (a.name.toLowerCase().substr(0,2)=='on')  {
25720                 node.removeAttribute(a.name);
25721                 continue;
25722             }
25723             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25724                 node.removeAttribute(a.name);
25725                 continue;
25726             }
25727             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25728                 cleanAttr(a.name,a.value); // fixme..
25729                 continue;
25730             }
25731             if (a.name == 'style') {
25732                 cleanStyle(a.name,a.value);
25733                 continue;
25734             }
25735             /// clean up MS crap..
25736             // tecnically this should be a list of valid class'es..
25737             
25738             
25739             if (a.name == 'class') {
25740                 if (a.value.match(/^Mso/)) {
25741                     node.className = '';
25742                 }
25743                 
25744                 if (a.value.match(/body/)) {
25745                     node.className = '';
25746                 }
25747                 continue;
25748             }
25749             
25750             // style cleanup!?
25751             // class cleanup?
25752             
25753         }
25754         
25755         
25756         this.cleanUpChildren(node);
25757         
25758         
25759     },
25760     /**
25761      * Clean up MS wordisms...
25762      */
25763     cleanWord : function(node)
25764     {
25765         var _t = this;
25766         var cleanWordChildren = function()
25767         {
25768             if (!node.childNodes.length) {
25769                 return;
25770             }
25771             for (var i = node.childNodes.length-1; i > -1 ; i--) {
25772                _t.cleanWord(node.childNodes[i]);
25773             }
25774         }
25775         
25776         
25777         if (!node) {
25778             this.cleanWord(this.doc.body);
25779             return;
25780         }
25781         if (node.nodeName == "#text") {
25782             // clean up silly Windows -- stuff?
25783             return; 
25784         }
25785         if (node.nodeName == "#comment") {
25786             node.parentNode.removeChild(node);
25787             // clean up silly Windows -- stuff?
25788             return; 
25789         }
25790         
25791         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25792             node.parentNode.removeChild(node);
25793             return;
25794         }
25795         
25796         // remove - but keep children..
25797         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25798             while (node.childNodes.length) {
25799                 var cn = node.childNodes[0];
25800                 node.removeChild(cn);
25801                 node.parentNode.insertBefore(cn, node);
25802             }
25803             node.parentNode.removeChild(node);
25804             cleanWordChildren();
25805             return;
25806         }
25807         // clean styles
25808         if (node.className.length) {
25809             
25810             var cn = node.className.split(/\W+/);
25811             var cna = [];
25812             Roo.each(cn, function(cls) {
25813                 if (cls.match(/Mso[a-zA-Z]+/)) {
25814                     return;
25815                 }
25816                 cna.push(cls);
25817             });
25818             node.className = cna.length ? cna.join(' ') : '';
25819             if (!cna.length) {
25820                 node.removeAttribute("class");
25821             }
25822         }
25823         
25824         if (node.hasAttribute("lang")) {
25825             node.removeAttribute("lang");
25826         }
25827         
25828         if (node.hasAttribute("style")) {
25829             
25830             var styles = node.getAttribute("style").split(";");
25831             var nstyle = [];
25832             Roo.each(styles, function(s) {
25833                 if (!s.match(/:/)) {
25834                     return;
25835                 }
25836                 var kv = s.split(":");
25837                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25838                     return;
25839                 }
25840                 // what ever is left... we allow.
25841                 nstyle.push(s);
25842             });
25843             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25844             if (!nstyle.length) {
25845                 node.removeAttribute('style');
25846             }
25847         }
25848         
25849         cleanWordChildren();
25850         
25851         
25852     },
25853     domToHTML : function(currentElement, depth, nopadtext) {
25854         
25855         depth = depth || 0;
25856         nopadtext = nopadtext || false;
25857     
25858         if (!currentElement) {
25859             return this.domToHTML(this.doc.body);
25860         }
25861         
25862         //Roo.log(currentElement);
25863         var j;
25864         var allText = false;
25865         var nodeName = currentElement.nodeName;
25866         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25867         
25868         if  (nodeName == '#text') {
25869             return currentElement.nodeValue;
25870         }
25871         
25872         
25873         var ret = '';
25874         if (nodeName != 'BODY') {
25875              
25876             var i = 0;
25877             // Prints the node tagName, such as <A>, <IMG>, etc
25878             if (tagName) {
25879                 var attr = [];
25880                 for(i = 0; i < currentElement.attributes.length;i++) {
25881                     // quoting?
25882                     var aname = currentElement.attributes.item(i).name;
25883                     if (!currentElement.attributes.item(i).value.length) {
25884                         continue;
25885                     }
25886                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25887                 }
25888                 
25889                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25890             } 
25891             else {
25892                 
25893                 // eack
25894             }
25895         } else {
25896             tagName = false;
25897         }
25898         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25899             return ret;
25900         }
25901         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25902             nopadtext = true;
25903         }
25904         
25905         
25906         // Traverse the tree
25907         i = 0;
25908         var currentElementChild = currentElement.childNodes.item(i);
25909         var allText = true;
25910         var innerHTML  = '';
25911         lastnode = '';
25912         while (currentElementChild) {
25913             // Formatting code (indent the tree so it looks nice on the screen)
25914             var nopad = nopadtext;
25915             if (lastnode == 'SPAN') {
25916                 nopad  = true;
25917             }
25918             // text
25919             if  (currentElementChild.nodeName == '#text') {
25920                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25921                 if (!nopad && toadd.length > 80) {
25922                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25923                 }
25924                 innerHTML  += toadd;
25925                 
25926                 i++;
25927                 currentElementChild = currentElement.childNodes.item(i);
25928                 lastNode = '';
25929                 continue;
25930             }
25931             allText = false;
25932             
25933             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25934                 
25935             // Recursively traverse the tree structure of the child node
25936             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25937             lastnode = currentElementChild.nodeName;
25938             i++;
25939             currentElementChild=currentElement.childNodes.item(i);
25940         }
25941         
25942         ret += innerHTML;
25943         
25944         if (!allText) {
25945                 // The remaining code is mostly for formatting the tree
25946             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25947         }
25948         
25949         
25950         if (tagName) {
25951             ret+= "</"+tagName+">";
25952         }
25953         return ret;
25954         
25955     },
25956         
25957     applyBlacklists : function()
25958     {
25959         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25960         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25961         
25962         this.white = [];
25963         this.black = [];
25964         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25965             if (b.indexOf(tag) > -1) {
25966                 return;
25967             }
25968             this.white.push(tag);
25969             
25970         }, this);
25971         
25972         Roo.each(w, function(tag) {
25973             if (b.indexOf(tag) > -1) {
25974                 return;
25975             }
25976             if (this.white.indexOf(tag) > -1) {
25977                 return;
25978             }
25979             this.white.push(tag);
25980             
25981         }, this);
25982         
25983         
25984         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25985             if (w.indexOf(tag) > -1) {
25986                 return;
25987             }
25988             this.black.push(tag);
25989             
25990         }, this);
25991         
25992         Roo.each(b, function(tag) {
25993             if (w.indexOf(tag) > -1) {
25994                 return;
25995             }
25996             if (this.black.indexOf(tag) > -1) {
25997                 return;
25998             }
25999             this.black.push(tag);
26000             
26001         }, this);
26002         
26003         
26004         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26005         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26006         
26007         this.cwhite = [];
26008         this.cblack = [];
26009         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26010             if (b.indexOf(tag) > -1) {
26011                 return;
26012             }
26013             this.cwhite.push(tag);
26014             
26015         }, this);
26016         
26017         Roo.each(w, function(tag) {
26018             if (b.indexOf(tag) > -1) {
26019                 return;
26020             }
26021             if (this.cwhite.indexOf(tag) > -1) {
26022                 return;
26023             }
26024             this.cwhite.push(tag);
26025             
26026         }, this);
26027         
26028         
26029         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26030             if (w.indexOf(tag) > -1) {
26031                 return;
26032             }
26033             this.cblack.push(tag);
26034             
26035         }, this);
26036         
26037         Roo.each(b, function(tag) {
26038             if (w.indexOf(tag) > -1) {
26039                 return;
26040             }
26041             if (this.cblack.indexOf(tag) > -1) {
26042                 return;
26043             }
26044             this.cblack.push(tag);
26045             
26046         }, this);
26047     }
26048     
26049     // hide stuff that is not compatible
26050     /**
26051      * @event blur
26052      * @hide
26053      */
26054     /**
26055      * @event change
26056      * @hide
26057      */
26058     /**
26059      * @event focus
26060      * @hide
26061      */
26062     /**
26063      * @event specialkey
26064      * @hide
26065      */
26066     /**
26067      * @cfg {String} fieldClass @hide
26068      */
26069     /**
26070      * @cfg {String} focusClass @hide
26071      */
26072     /**
26073      * @cfg {String} autoCreate @hide
26074      */
26075     /**
26076      * @cfg {String} inputType @hide
26077      */
26078     /**
26079      * @cfg {String} invalidClass @hide
26080      */
26081     /**
26082      * @cfg {String} invalidText @hide
26083      */
26084     /**
26085      * @cfg {String} msgFx @hide
26086      */
26087     /**
26088      * @cfg {String} validateOnBlur @hide
26089      */
26090 });
26091
26092 Roo.HtmlEditorCore.white = [
26093         'area', 'br', 'img', 'input', 'hr', 'wbr',
26094         
26095        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26096        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26097        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26098        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26099        'table',   'ul',         'xmp', 
26100        
26101        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26102       'thead',   'tr', 
26103      
26104       'dir', 'menu', 'ol', 'ul', 'dl',
26105        
26106       'embed',  'object'
26107 ];
26108
26109
26110 Roo.HtmlEditorCore.black = [
26111     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26112         'applet', // 
26113         'base',   'basefont', 'bgsound', 'blink',  'body', 
26114         'frame',  'frameset', 'head',    'html',   'ilayer', 
26115         'iframe', 'layer',  'link',     'meta',    'object',   
26116         'script', 'style' ,'title',  'xml' // clean later..
26117 ];
26118 Roo.HtmlEditorCore.clean = [
26119     'script', 'style', 'title', 'xml'
26120 ];
26121 Roo.HtmlEditorCore.remove = [
26122     'font'
26123 ];
26124 // attributes..
26125
26126 Roo.HtmlEditorCore.ablack = [
26127     'on'
26128 ];
26129     
26130 Roo.HtmlEditorCore.aclean = [ 
26131     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26132 ];
26133
26134 // protocols..
26135 Roo.HtmlEditorCore.pwhite= [
26136         'http',  'https',  'mailto'
26137 ];
26138
26139 // white listed style attributes.
26140 Roo.HtmlEditorCore.cwhite= [
26141       //  'text-align', /// default is to allow most things..
26142       
26143          
26144 //        'font-size'//??
26145 ];
26146
26147 // black listed style attributes.
26148 Roo.HtmlEditorCore.cblack= [
26149       //  'font-size' -- this can be set by the project 
26150 ];
26151
26152
26153 Roo.HtmlEditorCore.swapCodes   =[ 
26154     [    8211, "--" ], 
26155     [    8212, "--" ], 
26156     [    8216,  "'" ],  
26157     [    8217, "'" ],  
26158     [    8220, '"' ],  
26159     [    8221, '"' ],  
26160     [    8226, "*" ],  
26161     [    8230, "..." ]
26162 ]; 
26163
26164     //<script type="text/javascript">
26165
26166 /*
26167  * Ext JS Library 1.1.1
26168  * Copyright(c) 2006-2007, Ext JS, LLC.
26169  * Licence LGPL
26170  * 
26171  */
26172  
26173  
26174 Roo.form.HtmlEditor = function(config){
26175     
26176     
26177     
26178     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26179     
26180     if (!this.toolbars) {
26181         this.toolbars = [];
26182     }
26183     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26184     
26185     
26186 };
26187
26188 /**
26189  * @class Roo.form.HtmlEditor
26190  * @extends Roo.form.Field
26191  * Provides a lightweight HTML Editor component.
26192  *
26193  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26194  * 
26195  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26196  * supported by this editor.</b><br/><br/>
26197  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26198  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26199  */
26200 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26201     /**
26202      * @cfg {Boolean} clearUp
26203      */
26204     clearUp : true,
26205       /**
26206      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26207      */
26208     toolbars : false,
26209    
26210      /**
26211      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26212      *                        Roo.resizable.
26213      */
26214     resizable : false,
26215      /**
26216      * @cfg {Number} height (in pixels)
26217      */   
26218     height: 300,
26219    /**
26220      * @cfg {Number} width (in pixels)
26221      */   
26222     width: 500,
26223     
26224     /**
26225      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26226      * 
26227      */
26228     stylesheets: false,
26229     
26230     
26231      /**
26232      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26233      * 
26234      */
26235     cblack: false,
26236     /**
26237      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26238      * 
26239      */
26240     cwhite: false,
26241     
26242      /**
26243      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26244      * 
26245      */
26246     black: false,
26247     /**
26248      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26249      * 
26250      */
26251     white: false,
26252     
26253     // id of frame..
26254     frameId: false,
26255     
26256     // private properties
26257     validationEvent : false,
26258     deferHeight: true,
26259     initialized : false,
26260     activated : false,
26261     
26262     onFocus : Roo.emptyFn,
26263     iframePad:3,
26264     hideMode:'offsets',
26265     
26266     actionMode : 'container', // defaults to hiding it...
26267     
26268     defaultAutoCreate : { // modified by initCompnoent..
26269         tag: "textarea",
26270         style:"width:500px;height:300px;",
26271         autocomplete: "off"
26272     },
26273
26274     // private
26275     initComponent : function(){
26276         this.addEvents({
26277             /**
26278              * @event initialize
26279              * Fires when the editor is fully initialized (including the iframe)
26280              * @param {HtmlEditor} this
26281              */
26282             initialize: true,
26283             /**
26284              * @event activate
26285              * Fires when the editor is first receives the focus. Any insertion must wait
26286              * until after this event.
26287              * @param {HtmlEditor} this
26288              */
26289             activate: true,
26290              /**
26291              * @event beforesync
26292              * Fires before the textarea is updated with content from the editor iframe. Return false
26293              * to cancel the sync.
26294              * @param {HtmlEditor} this
26295              * @param {String} html
26296              */
26297             beforesync: true,
26298              /**
26299              * @event beforepush
26300              * Fires before the iframe editor is updated with content from the textarea. Return false
26301              * to cancel the push.
26302              * @param {HtmlEditor} this
26303              * @param {String} html
26304              */
26305             beforepush: true,
26306              /**
26307              * @event sync
26308              * Fires when the textarea is updated with content from the editor iframe.
26309              * @param {HtmlEditor} this
26310              * @param {String} html
26311              */
26312             sync: true,
26313              /**
26314              * @event push
26315              * Fires when the iframe editor is updated with content from the textarea.
26316              * @param {HtmlEditor} this
26317              * @param {String} html
26318              */
26319             push: true,
26320              /**
26321              * @event editmodechange
26322              * Fires when the editor switches edit modes
26323              * @param {HtmlEditor} this
26324              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26325              */
26326             editmodechange: true,
26327             /**
26328              * @event editorevent
26329              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26330              * @param {HtmlEditor} this
26331              */
26332             editorevent: true,
26333             /**
26334              * @event firstfocus
26335              * Fires when on first focus - needed by toolbars..
26336              * @param {HtmlEditor} this
26337              */
26338             firstfocus: true,
26339             /**
26340              * @event autosave
26341              * Auto save the htmlEditor value as a file into Events
26342              * @param {HtmlEditor} this
26343              */
26344             autosave: true,
26345             /**
26346              * @event savedpreview
26347              * preview the saved version of htmlEditor
26348              * @param {HtmlEditor} this
26349              */
26350             savedpreview: true
26351         });
26352         this.defaultAutoCreate =  {
26353             tag: "textarea",
26354             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26355             autocomplete: "off"
26356         };
26357     },
26358
26359     /**
26360      * Protected method that will not generally be called directly. It
26361      * is called when the editor creates its toolbar. Override this method if you need to
26362      * add custom toolbar buttons.
26363      * @param {HtmlEditor} editor
26364      */
26365     createToolbar : function(editor){
26366         Roo.log("create toolbars");
26367         if (!editor.toolbars || !editor.toolbars.length) {
26368             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26369         }
26370         
26371         for (var i =0 ; i < editor.toolbars.length;i++) {
26372             editor.toolbars[i] = Roo.factory(
26373                     typeof(editor.toolbars[i]) == 'string' ?
26374                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26375                 Roo.form.HtmlEditor);
26376             editor.toolbars[i].init(editor);
26377         }
26378          
26379         
26380     },
26381
26382      
26383     // private
26384     onRender : function(ct, position)
26385     {
26386         var _t = this;
26387         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26388         
26389         this.wrap = this.el.wrap({
26390             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26391         });
26392         
26393         this.editorcore.onRender(ct, position);
26394          
26395         if (this.resizable) {
26396             this.resizeEl = new Roo.Resizable(this.wrap, {
26397                 pinned : true,
26398                 wrap: true,
26399                 dynamic : true,
26400                 minHeight : this.height,
26401                 height: this.height,
26402                 handles : this.resizable,
26403                 width: this.width,
26404                 listeners : {
26405                     resize : function(r, w, h) {
26406                         _t.onResize(w,h); // -something
26407                     }
26408                 }
26409             });
26410             
26411         }
26412         this.createToolbar(this);
26413        
26414         
26415         if(!this.width){
26416             this.setSize(this.wrap.getSize());
26417         }
26418         if (this.resizeEl) {
26419             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26420             // should trigger onReize..
26421         }
26422         
26423 //        if(this.autosave && this.w){
26424 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26425 //        }
26426     },
26427
26428     // private
26429     onResize : function(w, h)
26430     {
26431         //Roo.log('resize: ' +w + ',' + h );
26432         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26433         var ew = false;
26434         var eh = false;
26435         
26436         if(this.el ){
26437             if(typeof w == 'number'){
26438                 var aw = w - this.wrap.getFrameWidth('lr');
26439                 this.el.setWidth(this.adjustWidth('textarea', aw));
26440                 ew = aw;
26441             }
26442             if(typeof h == 'number'){
26443                 var tbh = 0;
26444                 for (var i =0; i < this.toolbars.length;i++) {
26445                     // fixme - ask toolbars for heights?
26446                     tbh += this.toolbars[i].tb.el.getHeight();
26447                     if (this.toolbars[i].footer) {
26448                         tbh += this.toolbars[i].footer.el.getHeight();
26449                     }
26450                 }
26451                 
26452                 
26453                 
26454                 
26455                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26456                 ah -= 5; // knock a few pixes off for look..
26457                 this.el.setHeight(this.adjustWidth('textarea', ah));
26458                 var eh = ah;
26459             }
26460         }
26461         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26462         this.editorcore.onResize(ew,eh);
26463         
26464     },
26465
26466     /**
26467      * Toggles the editor between standard and source edit mode.
26468      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26469      */
26470     toggleSourceEdit : function(sourceEditMode)
26471     {
26472         this.editorcore.toggleSourceEdit(sourceEditMode);
26473         
26474         if(this.editorcore.sourceEditMode){
26475             Roo.log('editor - showing textarea');
26476             
26477 //            Roo.log('in');
26478 //            Roo.log(this.syncValue());
26479             this.editorcore.syncValue();
26480             this.el.removeClass('x-hidden');
26481             this.el.dom.removeAttribute('tabIndex');
26482             this.el.focus();
26483         }else{
26484             Roo.log('editor - hiding textarea');
26485 //            Roo.log('out')
26486 //            Roo.log(this.pushValue()); 
26487             this.editorcore.pushValue();
26488             
26489             this.el.addClass('x-hidden');
26490             this.el.dom.setAttribute('tabIndex', -1);
26491             //this.deferFocus();
26492         }
26493          
26494         this.setSize(this.wrap.getSize());
26495         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26496     },
26497  
26498     // private (for BoxComponent)
26499     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26500
26501     // private (for BoxComponent)
26502     getResizeEl : function(){
26503         return this.wrap;
26504     },
26505
26506     // private (for BoxComponent)
26507     getPositionEl : function(){
26508         return this.wrap;
26509     },
26510
26511     // private
26512     initEvents : function(){
26513         this.originalValue = this.getValue();
26514     },
26515
26516     /**
26517      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26518      * @method
26519      */
26520     markInvalid : Roo.emptyFn,
26521     /**
26522      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26523      * @method
26524      */
26525     clearInvalid : Roo.emptyFn,
26526
26527     setValue : function(v){
26528         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26529         this.editorcore.pushValue();
26530     },
26531
26532      
26533     // private
26534     deferFocus : function(){
26535         this.focus.defer(10, this);
26536     },
26537
26538     // doc'ed in Field
26539     focus : function(){
26540         this.editorcore.focus();
26541         
26542     },
26543       
26544
26545     // private
26546     onDestroy : function(){
26547         
26548         
26549         
26550         if(this.rendered){
26551             
26552             for (var i =0; i < this.toolbars.length;i++) {
26553                 // fixme - ask toolbars for heights?
26554                 this.toolbars[i].onDestroy();
26555             }
26556             
26557             this.wrap.dom.innerHTML = '';
26558             this.wrap.remove();
26559         }
26560     },
26561
26562     // private
26563     onFirstFocus : function(){
26564         //Roo.log("onFirstFocus");
26565         this.editorcore.onFirstFocus();
26566          for (var i =0; i < this.toolbars.length;i++) {
26567             this.toolbars[i].onFirstFocus();
26568         }
26569         
26570     },
26571     
26572     // private
26573     syncValue : function()
26574     {
26575         this.editorcore.syncValue();
26576     },
26577     
26578     pushValue : function()
26579     {
26580         this.editorcore.pushValue();
26581     }
26582      
26583     
26584     // hide stuff that is not compatible
26585     /**
26586      * @event blur
26587      * @hide
26588      */
26589     /**
26590      * @event change
26591      * @hide
26592      */
26593     /**
26594      * @event focus
26595      * @hide
26596      */
26597     /**
26598      * @event specialkey
26599      * @hide
26600      */
26601     /**
26602      * @cfg {String} fieldClass @hide
26603      */
26604     /**
26605      * @cfg {String} focusClass @hide
26606      */
26607     /**
26608      * @cfg {String} autoCreate @hide
26609      */
26610     /**
26611      * @cfg {String} inputType @hide
26612      */
26613     /**
26614      * @cfg {String} invalidClass @hide
26615      */
26616     /**
26617      * @cfg {String} invalidText @hide
26618      */
26619     /**
26620      * @cfg {String} msgFx @hide
26621      */
26622     /**
26623      * @cfg {String} validateOnBlur @hide
26624      */
26625 });
26626  
26627     // <script type="text/javascript">
26628 /*
26629  * Based on
26630  * Ext JS Library 1.1.1
26631  * Copyright(c) 2006-2007, Ext JS, LLC.
26632  *  
26633  
26634  */
26635
26636 /**
26637  * @class Roo.form.HtmlEditorToolbar1
26638  * Basic Toolbar
26639  * 
26640  * Usage:
26641  *
26642  new Roo.form.HtmlEditor({
26643     ....
26644     toolbars : [
26645         new Roo.form.HtmlEditorToolbar1({
26646             disable : { fonts: 1 , format: 1, ..., ... , ...],
26647             btns : [ .... ]
26648         })
26649     }
26650      
26651  * 
26652  * @cfg {Object} disable List of elements to disable..
26653  * @cfg {Array} btns List of additional buttons.
26654  * 
26655  * 
26656  * NEEDS Extra CSS? 
26657  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26658  */
26659  
26660 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26661 {
26662     
26663     Roo.apply(this, config);
26664     
26665     // default disabled, based on 'good practice'..
26666     this.disable = this.disable || {};
26667     Roo.applyIf(this.disable, {
26668         fontSize : true,
26669         colors : true,
26670         specialElements : true
26671     });
26672     
26673     
26674     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26675     // dont call parent... till later.
26676 }
26677
26678 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26679     
26680     tb: false,
26681     
26682     rendered: false,
26683     
26684     editor : false,
26685     editorcore : false,
26686     /**
26687      * @cfg {Object} disable  List of toolbar elements to disable
26688          
26689      */
26690     disable : false,
26691     
26692     
26693      /**
26694      * @cfg {String} createLinkText The default text for the create link prompt
26695      */
26696     createLinkText : 'Please enter the URL for the link:',
26697     /**
26698      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26699      */
26700     defaultLinkValue : 'http:/'+'/',
26701    
26702     
26703       /**
26704      * @cfg {Array} fontFamilies An array of available font families
26705      */
26706     fontFamilies : [
26707         'Arial',
26708         'Courier New',
26709         'Tahoma',
26710         'Times New Roman',
26711         'Verdana'
26712     ],
26713     
26714     specialChars : [
26715            "&#169;",
26716           "&#174;",     
26717           "&#8482;",    
26718           "&#163;" ,    
26719          // "&#8212;",    
26720           "&#8230;",    
26721           "&#247;" ,    
26722         //  "&#225;" ,     ?? a acute?
26723            "&#8364;"    , //Euro
26724        //   "&#8220;"    ,
26725         //  "&#8221;"    ,
26726         //  "&#8226;"    ,
26727           "&#176;"  //   , // degrees
26728
26729          // "&#233;"     , // e ecute
26730          // "&#250;"     , // u ecute?
26731     ],
26732     
26733     specialElements : [
26734         {
26735             text: "Insert Table",
26736             xtype: 'MenuItem',
26737             xns : Roo.Menu,
26738             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26739                 
26740         },
26741         {    
26742             text: "Insert Image",
26743             xtype: 'MenuItem',
26744             xns : Roo.Menu,
26745             ihtml : '<img src="about:blank"/>'
26746             
26747         }
26748         
26749          
26750     ],
26751     
26752     
26753     inputElements : [ 
26754             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26755             "input:submit", "input:button", "select", "textarea", "label" ],
26756     formats : [
26757         ["p"] ,  
26758         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26759         ["pre"],[ "code"], 
26760         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26761         ['div'],['span']
26762     ],
26763     
26764     cleanStyles : [
26765         "font-size"
26766     ],
26767      /**
26768      * @cfg {String} defaultFont default font to use.
26769      */
26770     defaultFont: 'tahoma',
26771    
26772     fontSelect : false,
26773     
26774     
26775     formatCombo : false,
26776     
26777     init : function(editor)
26778     {
26779         this.editor = editor;
26780         this.editorcore = editor.editorcore ? editor.editorcore : editor;
26781         var editorcore = this.editorcore;
26782         
26783         var _t = this;
26784         
26785         var fid = editorcore.frameId;
26786         var etb = this;
26787         function btn(id, toggle, handler){
26788             var xid = fid + '-'+ id ;
26789             return {
26790                 id : xid,
26791                 cmd : id,
26792                 cls : 'x-btn-icon x-edit-'+id,
26793                 enableToggle:toggle !== false,
26794                 scope: _t, // was editor...
26795                 handler:handler||_t.relayBtnCmd,
26796                 clickEvent:'mousedown',
26797                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26798                 tabIndex:-1
26799             };
26800         }
26801         
26802         
26803         
26804         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26805         this.tb = tb;
26806          // stop form submits
26807         tb.el.on('click', function(e){
26808             e.preventDefault(); // what does this do?
26809         });
26810
26811         if(!this.disable.font) { // && !Roo.isSafari){
26812             /* why no safari for fonts 
26813             editor.fontSelect = tb.el.createChild({
26814                 tag:'select',
26815                 tabIndex: -1,
26816                 cls:'x-font-select',
26817                 html: this.createFontOptions()
26818             });
26819             
26820             editor.fontSelect.on('change', function(){
26821                 var font = editor.fontSelect.dom.value;
26822                 editor.relayCmd('fontname', font);
26823                 editor.deferFocus();
26824             }, editor);
26825             
26826             tb.add(
26827                 editor.fontSelect.dom,
26828                 '-'
26829             );
26830             */
26831             
26832         };
26833         if(!this.disable.formats){
26834             this.formatCombo = new Roo.form.ComboBox({
26835                 store: new Roo.data.SimpleStore({
26836                     id : 'tag',
26837                     fields: ['tag'],
26838                     data : this.formats // from states.js
26839                 }),
26840                 blockFocus : true,
26841                 name : '',
26842                 //autoCreate : {tag: "div",  size: "20"},
26843                 displayField:'tag',
26844                 typeAhead: false,
26845                 mode: 'local',
26846                 editable : false,
26847                 triggerAction: 'all',
26848                 emptyText:'Add tag',
26849                 selectOnFocus:true,
26850                 width:135,
26851                 listeners : {
26852                     'select': function(c, r, i) {
26853                         editorcore.insertTag(r.get('tag'));
26854                         editor.focus();
26855                     }
26856                 }
26857
26858             });
26859             tb.addField(this.formatCombo);
26860             
26861         }
26862         
26863         if(!this.disable.format){
26864             tb.add(
26865                 btn('bold'),
26866                 btn('italic'),
26867                 btn('underline')
26868             );
26869         };
26870         if(!this.disable.fontSize){
26871             tb.add(
26872                 '-',
26873                 
26874                 
26875                 btn('increasefontsize', false, editorcore.adjustFont),
26876                 btn('decreasefontsize', false, editorcore.adjustFont)
26877             );
26878         };
26879         
26880         
26881         if(!this.disable.colors){
26882             tb.add(
26883                 '-', {
26884                     id:editorcore.frameId +'-forecolor',
26885                     cls:'x-btn-icon x-edit-forecolor',
26886                     clickEvent:'mousedown',
26887                     tooltip: this.buttonTips['forecolor'] || undefined,
26888                     tabIndex:-1,
26889                     menu : new Roo.menu.ColorMenu({
26890                         allowReselect: true,
26891                         focus: Roo.emptyFn,
26892                         value:'000000',
26893                         plain:true,
26894                         selectHandler: function(cp, color){
26895                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26896                             editor.deferFocus();
26897                         },
26898                         scope: editorcore,
26899                         clickEvent:'mousedown'
26900                     })
26901                 }, {
26902                     id:editorcore.frameId +'backcolor',
26903                     cls:'x-btn-icon x-edit-backcolor',
26904                     clickEvent:'mousedown',
26905                     tooltip: this.buttonTips['backcolor'] || undefined,
26906                     tabIndex:-1,
26907                     menu : new Roo.menu.ColorMenu({
26908                         focus: Roo.emptyFn,
26909                         value:'FFFFFF',
26910                         plain:true,
26911                         allowReselect: true,
26912                         selectHandler: function(cp, color){
26913                             if(Roo.isGecko){
26914                                 editorcore.execCmd('useCSS', false);
26915                                 editorcore.execCmd('hilitecolor', color);
26916                                 editorcore.execCmd('useCSS', true);
26917                                 editor.deferFocus();
26918                             }else{
26919                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26920                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26921                                 editor.deferFocus();
26922                             }
26923                         },
26924                         scope:editorcore,
26925                         clickEvent:'mousedown'
26926                     })
26927                 }
26928             );
26929         };
26930         // now add all the items...
26931         
26932
26933         if(!this.disable.alignments){
26934             tb.add(
26935                 '-',
26936                 btn('justifyleft'),
26937                 btn('justifycenter'),
26938                 btn('justifyright')
26939             );
26940         };
26941
26942         //if(!Roo.isSafari){
26943             if(!this.disable.links){
26944                 tb.add(
26945                     '-',
26946                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
26947                 );
26948             };
26949
26950             if(!this.disable.lists){
26951                 tb.add(
26952                     '-',
26953                     btn('insertorderedlist'),
26954                     btn('insertunorderedlist')
26955                 );
26956             }
26957             if(!this.disable.sourceEdit){
26958                 tb.add(
26959                     '-',
26960                     btn('sourceedit', true, function(btn){
26961                         Roo.log(this);
26962                         this.toggleSourceEdit(btn.pressed);
26963                     })
26964                 );
26965             }
26966         //}
26967         
26968         var smenu = { };
26969         // special menu.. - needs to be tidied up..
26970         if (!this.disable.special) {
26971             smenu = {
26972                 text: "&#169;",
26973                 cls: 'x-edit-none',
26974                 
26975                 menu : {
26976                     items : []
26977                 }
26978             };
26979             for (var i =0; i < this.specialChars.length; i++) {
26980                 smenu.menu.items.push({
26981                     
26982                     html: this.specialChars[i],
26983                     handler: function(a,b) {
26984                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26985                         //editor.insertAtCursor(a.html);
26986                         
26987                     },
26988                     tabIndex:-1
26989                 });
26990             }
26991             
26992             
26993             tb.add(smenu);
26994             
26995             
26996         }
26997         
26998         var cmenu = { };
26999         if (!this.disable.cleanStyles) {
27000             cmenu = {
27001                 cls: 'x-btn-icon x-btn-clear',
27002                 
27003                 menu : {
27004                     items : []
27005                 }
27006             };
27007             for (var i =0; i < this.cleanStyles.length; i++) {
27008                 cmenu.menu.items.push({
27009                     actiontype : this.cleanStyles[i],
27010                     html: 'Remove ' + this.cleanStyles[i],
27011                     handler: function(a,b) {
27012                         Roo.log(a);
27013                         Roo.log(b);
27014                         var c = Roo.get(editorcore.doc.body);
27015                         c.select('[style]').each(function(s) {
27016                             s.dom.style.removeProperty(a.actiontype);
27017                         });
27018                         editorcore.syncValue();
27019                     },
27020                     tabIndex:-1
27021                 });
27022             }
27023             cmenu.menu.items.push({
27024                 actiontype : 'word',
27025                 html: 'Remove MS Word Formating',
27026                 handler: function(a,b) {
27027                     editorcore.cleanWord();
27028                     editorcore.syncValue();
27029                 },
27030                 tabIndex:-1
27031             });
27032             
27033             cmenu.menu.items.push({
27034                 actiontype : 'all',
27035                 html: 'Remove All Styles',
27036                 handler: function(a,b) {
27037                     
27038                     var c = Roo.get(editorcore.doc.body);
27039                     c.select('[style]').each(function(s) {
27040                         s.dom.removeAttribute('style');
27041                     });
27042                     editorcore.syncValue();
27043                 },
27044                 tabIndex:-1
27045             });
27046              cmenu.menu.items.push({
27047                 actiontype : 'word',
27048                 html: 'Tidy HTML Source',
27049                 handler: function(a,b) {
27050                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27051                     editorcore.syncValue();
27052                 },
27053                 tabIndex:-1
27054             });
27055             
27056             
27057             tb.add(cmenu);
27058         }
27059          
27060         if (!this.disable.specialElements) {
27061             var semenu = {
27062                 text: "Other;",
27063                 cls: 'x-edit-none',
27064                 menu : {
27065                     items : []
27066                 }
27067             };
27068             for (var i =0; i < this.specialElements.length; i++) {
27069                 semenu.menu.items.push(
27070                     Roo.apply({ 
27071                         handler: function(a,b) {
27072                             editor.insertAtCursor(this.ihtml);
27073                         }
27074                     }, this.specialElements[i])
27075                 );
27076                     
27077             }
27078             
27079             tb.add(semenu);
27080             
27081             
27082         }
27083          
27084         
27085         if (this.btns) {
27086             for(var i =0; i< this.btns.length;i++) {
27087                 var b = Roo.factory(this.btns[i],Roo.form);
27088                 b.cls =  'x-edit-none';
27089                 b.scope = editorcore;
27090                 tb.add(b);
27091             }
27092         
27093         }
27094         
27095         
27096         
27097         // disable everything...
27098         
27099         this.tb.items.each(function(item){
27100            if(item.id != editorcore.frameId+ '-sourceedit'){
27101                 item.disable();
27102             }
27103         });
27104         this.rendered = true;
27105         
27106         // the all the btns;
27107         editor.on('editorevent', this.updateToolbar, this);
27108         // other toolbars need to implement this..
27109         //editor.on('editmodechange', this.updateToolbar, this);
27110     },
27111     
27112     
27113     relayBtnCmd : function(btn) {
27114         this.editorcore.relayCmd(btn.cmd);
27115     },
27116     // private used internally
27117     createLink : function(){
27118         Roo.log("create link?");
27119         var url = prompt(this.createLinkText, this.defaultLinkValue);
27120         if(url && url != 'http:/'+'/'){
27121             this.editorcore.relayCmd('createlink', url);
27122         }
27123     },
27124
27125     
27126     /**
27127      * Protected method that will not generally be called directly. It triggers
27128      * a toolbar update by reading the markup state of the current selection in the editor.
27129      */
27130     updateToolbar: function(){
27131
27132         if(!this.editorcore.activated){
27133             this.editor.onFirstFocus();
27134             return;
27135         }
27136
27137         var btns = this.tb.items.map, 
27138             doc = this.editorcore.doc,
27139             frameId = this.editorcore.frameId;
27140
27141         if(!this.disable.font && !Roo.isSafari){
27142             /*
27143             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27144             if(name != this.fontSelect.dom.value){
27145                 this.fontSelect.dom.value = name;
27146             }
27147             */
27148         }
27149         if(!this.disable.format){
27150             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27151             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27152             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27153         }
27154         if(!this.disable.alignments){
27155             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27156             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27157             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27158         }
27159         if(!Roo.isSafari && !this.disable.lists){
27160             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27161             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27162         }
27163         
27164         var ans = this.editorcore.getAllAncestors();
27165         if (this.formatCombo) {
27166             
27167             
27168             var store = this.formatCombo.store;
27169             this.formatCombo.setValue("");
27170             for (var i =0; i < ans.length;i++) {
27171                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27172                     // select it..
27173                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27174                     break;
27175                 }
27176             }
27177         }
27178         
27179         
27180         
27181         // hides menus... - so this cant be on a menu...
27182         Roo.menu.MenuMgr.hideAll();
27183
27184         //this.editorsyncValue();
27185     },
27186    
27187     
27188     createFontOptions : function(){
27189         var buf = [], fs = this.fontFamilies, ff, lc;
27190         
27191         
27192         
27193         for(var i = 0, len = fs.length; i< len; i++){
27194             ff = fs[i];
27195             lc = ff.toLowerCase();
27196             buf.push(
27197                 '<option value="',lc,'" style="font-family:',ff,';"',
27198                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27199                     ff,
27200                 '</option>'
27201             );
27202         }
27203         return buf.join('');
27204     },
27205     
27206     toggleSourceEdit : function(sourceEditMode){
27207         
27208         Roo.log("toolbar toogle");
27209         if(sourceEditMode === undefined){
27210             sourceEditMode = !this.sourceEditMode;
27211         }
27212         this.sourceEditMode = sourceEditMode === true;
27213         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27214         // just toggle the button?
27215         if(btn.pressed !== this.sourceEditMode){
27216             btn.toggle(this.sourceEditMode);
27217             return;
27218         }
27219         
27220         if(sourceEditMode){
27221             Roo.log("disabling buttons");
27222             this.tb.items.each(function(item){
27223                 if(item.cmd != 'sourceedit'){
27224                     item.disable();
27225                 }
27226             });
27227           
27228         }else{
27229             Roo.log("enabling buttons");
27230             if(this.editorcore.initialized){
27231                 this.tb.items.each(function(item){
27232                     item.enable();
27233                 });
27234             }
27235             
27236         }
27237         Roo.log("calling toggole on editor");
27238         // tell the editor that it's been pressed..
27239         this.editor.toggleSourceEdit(sourceEditMode);
27240        
27241     },
27242      /**
27243      * Object collection of toolbar tooltips for the buttons in the editor. The key
27244      * is the command id associated with that button and the value is a valid QuickTips object.
27245      * For example:
27246 <pre><code>
27247 {
27248     bold : {
27249         title: 'Bold (Ctrl+B)',
27250         text: 'Make the selected text bold.',
27251         cls: 'x-html-editor-tip'
27252     },
27253     italic : {
27254         title: 'Italic (Ctrl+I)',
27255         text: 'Make the selected text italic.',
27256         cls: 'x-html-editor-tip'
27257     },
27258     ...
27259 </code></pre>
27260     * @type Object
27261      */
27262     buttonTips : {
27263         bold : {
27264             title: 'Bold (Ctrl+B)',
27265             text: 'Make the selected text bold.',
27266             cls: 'x-html-editor-tip'
27267         },
27268         italic : {
27269             title: 'Italic (Ctrl+I)',
27270             text: 'Make the selected text italic.',
27271             cls: 'x-html-editor-tip'
27272         },
27273         underline : {
27274             title: 'Underline (Ctrl+U)',
27275             text: 'Underline the selected text.',
27276             cls: 'x-html-editor-tip'
27277         },
27278         increasefontsize : {
27279             title: 'Grow Text',
27280             text: 'Increase the font size.',
27281             cls: 'x-html-editor-tip'
27282         },
27283         decreasefontsize : {
27284             title: 'Shrink Text',
27285             text: 'Decrease the font size.',
27286             cls: 'x-html-editor-tip'
27287         },
27288         backcolor : {
27289             title: 'Text Highlight Color',
27290             text: 'Change the background color of the selected text.',
27291             cls: 'x-html-editor-tip'
27292         },
27293         forecolor : {
27294             title: 'Font Color',
27295             text: 'Change the color of the selected text.',
27296             cls: 'x-html-editor-tip'
27297         },
27298         justifyleft : {
27299             title: 'Align Text Left',
27300             text: 'Align text to the left.',
27301             cls: 'x-html-editor-tip'
27302         },
27303         justifycenter : {
27304             title: 'Center Text',
27305             text: 'Center text in the editor.',
27306             cls: 'x-html-editor-tip'
27307         },
27308         justifyright : {
27309             title: 'Align Text Right',
27310             text: 'Align text to the right.',
27311             cls: 'x-html-editor-tip'
27312         },
27313         insertunorderedlist : {
27314             title: 'Bullet List',
27315             text: 'Start a bulleted list.',
27316             cls: 'x-html-editor-tip'
27317         },
27318         insertorderedlist : {
27319             title: 'Numbered List',
27320             text: 'Start a numbered list.',
27321             cls: 'x-html-editor-tip'
27322         },
27323         createlink : {
27324             title: 'Hyperlink',
27325             text: 'Make the selected text a hyperlink.',
27326             cls: 'x-html-editor-tip'
27327         },
27328         sourceedit : {
27329             title: 'Source Edit',
27330             text: 'Switch to source editing mode.',
27331             cls: 'x-html-editor-tip'
27332         }
27333     },
27334     // private
27335     onDestroy : function(){
27336         if(this.rendered){
27337             
27338             this.tb.items.each(function(item){
27339                 if(item.menu){
27340                     item.menu.removeAll();
27341                     if(item.menu.el){
27342                         item.menu.el.destroy();
27343                     }
27344                 }
27345                 item.destroy();
27346             });
27347              
27348         }
27349     },
27350     onFirstFocus: function() {
27351         this.tb.items.each(function(item){
27352            item.enable();
27353         });
27354     }
27355 });
27356
27357
27358
27359
27360 // <script type="text/javascript">
27361 /*
27362  * Based on
27363  * Ext JS Library 1.1.1
27364  * Copyright(c) 2006-2007, Ext JS, LLC.
27365  *  
27366  
27367  */
27368
27369  
27370 /**
27371  * @class Roo.form.HtmlEditor.ToolbarContext
27372  * Context Toolbar
27373  * 
27374  * Usage:
27375  *
27376  new Roo.form.HtmlEditor({
27377     ....
27378     toolbars : [
27379         { xtype: 'ToolbarStandard', styles : {} }
27380         { xtype: 'ToolbarContext', disable : {} }
27381     ]
27382 })
27383
27384      
27385  * 
27386  * @config : {Object} disable List of elements to disable.. (not done yet.)
27387  * @config : {Object} styles  Map of styles available.
27388  * 
27389  */
27390
27391 Roo.form.HtmlEditor.ToolbarContext = function(config)
27392 {
27393     
27394     Roo.apply(this, config);
27395     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27396     // dont call parent... till later.
27397     this.styles = this.styles || {};
27398 }
27399
27400  
27401
27402 Roo.form.HtmlEditor.ToolbarContext.types = {
27403     'IMG' : {
27404         width : {
27405             title: "Width",
27406             width: 40
27407         },
27408         height:  {
27409             title: "Height",
27410             width: 40
27411         },
27412         align: {
27413             title: "Align",
27414             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27415             width : 80
27416             
27417         },
27418         border: {
27419             title: "Border",
27420             width: 40
27421         },
27422         alt: {
27423             title: "Alt",
27424             width: 120
27425         },
27426         src : {
27427             title: "Src",
27428             width: 220
27429         }
27430         
27431     },
27432     'A' : {
27433         name : {
27434             title: "Name",
27435             width: 50
27436         },
27437         target:  {
27438             title: "Target",
27439             width: 120
27440         },
27441         href:  {
27442             title: "Href",
27443             width: 220
27444         } // border?
27445         
27446     },
27447     'TABLE' : {
27448         rows : {
27449             title: "Rows",
27450             width: 20
27451         },
27452         cols : {
27453             title: "Cols",
27454             width: 20
27455         },
27456         width : {
27457             title: "Width",
27458             width: 40
27459         },
27460         height : {
27461             title: "Height",
27462             width: 40
27463         },
27464         border : {
27465             title: "Border",
27466             width: 20
27467         }
27468     },
27469     'TD' : {
27470         width : {
27471             title: "Width",
27472             width: 40
27473         },
27474         height : {
27475             title: "Height",
27476             width: 40
27477         },   
27478         align: {
27479             title: "Align",
27480             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27481             width: 80
27482         },
27483         valign: {
27484             title: "Valign",
27485             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27486             width: 80
27487         },
27488         colspan: {
27489             title: "Colspan",
27490             width: 20
27491             
27492         },
27493          'font-family'  : {
27494             title : "Font",
27495             style : 'fontFamily',
27496             displayField: 'display',
27497             optname : 'font-family',
27498             width: 140
27499         }
27500     },
27501     'INPUT' : {
27502         name : {
27503             title: "name",
27504             width: 120
27505         },
27506         value : {
27507             title: "Value",
27508             width: 120
27509         },
27510         width : {
27511             title: "Width",
27512             width: 40
27513         }
27514     },
27515     'LABEL' : {
27516         'for' : {
27517             title: "For",
27518             width: 120
27519         }
27520     },
27521     'TEXTAREA' : {
27522           name : {
27523             title: "name",
27524             width: 120
27525         },
27526         rows : {
27527             title: "Rows",
27528             width: 20
27529         },
27530         cols : {
27531             title: "Cols",
27532             width: 20
27533         }
27534     },
27535     'SELECT' : {
27536         name : {
27537             title: "name",
27538             width: 120
27539         },
27540         selectoptions : {
27541             title: "Options",
27542             width: 200
27543         }
27544     },
27545     
27546     // should we really allow this??
27547     // should this just be 
27548     'BODY' : {
27549         title : {
27550             title: "Title",
27551             width: 200,
27552             disabled : true
27553         }
27554     },
27555     'SPAN' : {
27556         'font-family'  : {
27557             title : "Font",
27558             style : 'fontFamily',
27559             displayField: 'display',
27560             optname : 'font-family',
27561             width: 140
27562         }
27563     },
27564     'DIV' : {
27565         'font-family'  : {
27566             title : "Font",
27567             style : 'fontFamily',
27568             displayField: 'display',
27569             optname : 'font-family',
27570             width: 140
27571         }
27572     },
27573      'P' : {
27574         'font-family'  : {
27575             title : "Font",
27576             style : 'fontFamily',
27577             displayField: 'display',
27578             optname : 'font-family',
27579             width: 140
27580         }
27581     },
27582     
27583     '*' : {
27584         // empty..
27585     }
27586
27587 };
27588
27589 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27590 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27591
27592 Roo.form.HtmlEditor.ToolbarContext.options = {
27593         'font-family'  : [ 
27594                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27595                 [ 'Courier New', 'Courier New'],
27596                 [ 'Tahoma', 'Tahoma'],
27597                 [ 'Times New Roman,serif', 'Times'],
27598                 [ 'Verdana','Verdana' ]
27599         ]
27600 };
27601
27602 // fixme - these need to be configurable..
27603  
27604
27605 Roo.form.HtmlEditor.ToolbarContext.types
27606
27607
27608 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27609     
27610     tb: false,
27611     
27612     rendered: false,
27613     
27614     editor : false,
27615     editorcore : false,
27616     /**
27617      * @cfg {Object} disable  List of toolbar elements to disable
27618          
27619      */
27620     disable : false,
27621     /**
27622      * @cfg {Object} styles List of styles 
27623      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27624      *
27625      * These must be defined in the page, so they get rendered correctly..
27626      * .headline { }
27627      * TD.underline { }
27628      * 
27629      */
27630     styles : false,
27631     
27632     options: false,
27633     
27634     toolbars : false,
27635     
27636     init : function(editor)
27637     {
27638         this.editor = editor;
27639         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27640         var editorcore = this.editorcore;
27641         
27642         var fid = editorcore.frameId;
27643         var etb = this;
27644         function btn(id, toggle, handler){
27645             var xid = fid + '-'+ id ;
27646             return {
27647                 id : xid,
27648                 cmd : id,
27649                 cls : 'x-btn-icon x-edit-'+id,
27650                 enableToggle:toggle !== false,
27651                 scope: editorcore, // was editor...
27652                 handler:handler||editorcore.relayBtnCmd,
27653                 clickEvent:'mousedown',
27654                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27655                 tabIndex:-1
27656             };
27657         }
27658         // create a new element.
27659         var wdiv = editor.wrap.createChild({
27660                 tag: 'div'
27661             }, editor.wrap.dom.firstChild.nextSibling, true);
27662         
27663         // can we do this more than once??
27664         
27665          // stop form submits
27666       
27667  
27668         // disable everything...
27669         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27670         this.toolbars = {};
27671            
27672         for (var i in  ty) {
27673           
27674             this.toolbars[i] = this.buildToolbar(ty[i],i);
27675         }
27676         this.tb = this.toolbars.BODY;
27677         this.tb.el.show();
27678         this.buildFooter();
27679         this.footer.show();
27680         editor.on('hide', function( ) { this.footer.hide() }, this);
27681         editor.on('show', function( ) { this.footer.show() }, this);
27682         
27683          
27684         this.rendered = true;
27685         
27686         // the all the btns;
27687         editor.on('editorevent', this.updateToolbar, this);
27688         // other toolbars need to implement this..
27689         //editor.on('editmodechange', this.updateToolbar, this);
27690     },
27691     
27692     
27693     
27694     /**
27695      * Protected method that will not generally be called directly. It triggers
27696      * a toolbar update by reading the markup state of the current selection in the editor.
27697      */
27698     updateToolbar: function(editor,ev,sel){
27699
27700         //Roo.log(ev);
27701         // capture mouse up - this is handy for selecting images..
27702         // perhaps should go somewhere else...
27703         if(!this.editorcore.activated){
27704              this.editor.onFirstFocus();
27705             return;
27706         }
27707         
27708         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27709         // selectNode - might want to handle IE?
27710         if (ev &&
27711             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27712             ev.target && ev.target.tagName == 'IMG') {
27713             // they have click on an image...
27714             // let's see if we can change the selection...
27715             sel = ev.target;
27716          
27717               var nodeRange = sel.ownerDocument.createRange();
27718             try {
27719                 nodeRange.selectNode(sel);
27720             } catch (e) {
27721                 nodeRange.selectNodeContents(sel);
27722             }
27723             //nodeRange.collapse(true);
27724             var s = this.editorcore.win.getSelection();
27725             s.removeAllRanges();
27726             s.addRange(nodeRange);
27727         }  
27728         
27729       
27730         var updateFooter = sel ? false : true;
27731         
27732         
27733         var ans = this.editorcore.getAllAncestors();
27734         
27735         // pick
27736         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27737         
27738         if (!sel) { 
27739             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
27740             sel = sel ? sel : this.editorcore.doc.body;
27741             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
27742             
27743         }
27744         // pick a menu that exists..
27745         var tn = sel.tagName.toUpperCase();
27746         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27747         
27748         tn = sel.tagName.toUpperCase();
27749         
27750         var lastSel = this.tb.selectedNode
27751         
27752         this.tb.selectedNode = sel;
27753         
27754         // if current menu does not match..
27755         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27756                 
27757             this.tb.el.hide();
27758             ///console.log("show: " + tn);
27759             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27760             this.tb.el.show();
27761             // update name
27762             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27763             
27764             
27765             // update attributes
27766             if (this.tb.fields) {
27767                 this.tb.fields.each(function(e) {
27768                     if (e.stylename) {
27769                         e.setValue(sel.style[e.stylename]);
27770                         return;
27771                     } 
27772                    e.setValue(sel.getAttribute(e.attrname));
27773                 });
27774             }
27775             
27776             var hasStyles = false;
27777             for(var i in this.styles) {
27778                 hasStyles = true;
27779                 break;
27780             }
27781             
27782             // update styles
27783             if (hasStyles) { 
27784                 var st = this.tb.fields.item(0);
27785                 
27786                 st.store.removeAll();
27787                
27788                 
27789                 var cn = sel.className.split(/\s+/);
27790                 
27791                 var avs = [];
27792                 if (this.styles['*']) {
27793                     
27794                     Roo.each(this.styles['*'], function(v) {
27795                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27796                     });
27797                 }
27798                 if (this.styles[tn]) { 
27799                     Roo.each(this.styles[tn], function(v) {
27800                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27801                     });
27802                 }
27803                 
27804                 st.store.loadData(avs);
27805                 st.collapse();
27806                 st.setValue(cn);
27807             }
27808             // flag our selected Node.
27809             this.tb.selectedNode = sel;
27810            
27811            
27812             Roo.menu.MenuMgr.hideAll();
27813
27814         }
27815         
27816         if (!updateFooter) {
27817             //this.footDisp.dom.innerHTML = ''; 
27818             return;
27819         }
27820         // update the footer
27821         //
27822         var html = '';
27823         
27824         this.footerEls = ans.reverse();
27825         Roo.each(this.footerEls, function(a,i) {
27826             if (!a) { return; }
27827             html += html.length ? ' &gt; '  :  '';
27828             
27829             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27830             
27831         });
27832        
27833         // 
27834         var sz = this.footDisp.up('td').getSize();
27835         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27836         this.footDisp.dom.style.marginLeft = '5px';
27837         
27838         this.footDisp.dom.style.overflow = 'hidden';
27839         
27840         this.footDisp.dom.innerHTML = html;
27841             
27842         //this.editorsyncValue();
27843     },
27844      
27845     
27846    
27847        
27848     // private
27849     onDestroy : function(){
27850         if(this.rendered){
27851             
27852             this.tb.items.each(function(item){
27853                 if(item.menu){
27854                     item.menu.removeAll();
27855                     if(item.menu.el){
27856                         item.menu.el.destroy();
27857                     }
27858                 }
27859                 item.destroy();
27860             });
27861              
27862         }
27863     },
27864     onFirstFocus: function() {
27865         // need to do this for all the toolbars..
27866         this.tb.items.each(function(item){
27867            item.enable();
27868         });
27869     },
27870     buildToolbar: function(tlist, nm)
27871     {
27872         var editor = this.editor;
27873         var editorcore = this.editorcore;
27874          // create a new element.
27875         var wdiv = editor.wrap.createChild({
27876                 tag: 'div'
27877             }, editor.wrap.dom.firstChild.nextSibling, true);
27878         
27879        
27880         var tb = new Roo.Toolbar(wdiv);
27881         // add the name..
27882         
27883         tb.add(nm+ ":&nbsp;");
27884         
27885         var styles = [];
27886         for(var i in this.styles) {
27887             styles.push(i);
27888         }
27889         
27890         // styles...
27891         if (styles && styles.length) {
27892             
27893             // this needs a multi-select checkbox...
27894             tb.addField( new Roo.form.ComboBox({
27895                 store: new Roo.data.SimpleStore({
27896                     id : 'val',
27897                     fields: ['val', 'selected'],
27898                     data : [] 
27899                 }),
27900                 name : '-roo-edit-className',
27901                 attrname : 'className',
27902                 displayField: 'val',
27903                 typeAhead: false,
27904                 mode: 'local',
27905                 editable : false,
27906                 triggerAction: 'all',
27907                 emptyText:'Select Style',
27908                 selectOnFocus:true,
27909                 width: 130,
27910                 listeners : {
27911                     'select': function(c, r, i) {
27912                         // initial support only for on class per el..
27913                         tb.selectedNode.className =  r ? r.get('val') : '';
27914                         editorcore.syncValue();
27915                     }
27916                 }
27917     
27918             }));
27919         }
27920         
27921         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27922         var tbops = tbc.options;
27923         
27924         for (var i in tlist) {
27925             
27926             var item = tlist[i];
27927             tb.add(item.title + ":&nbsp;");
27928             
27929             
27930             //optname == used so you can configure the options available..
27931             var opts = item.opts ? item.opts : false;
27932             if (item.optname) {
27933                 opts = tbops[item.optname];
27934            
27935             }
27936             
27937             if (opts) {
27938                 // opts == pulldown..
27939                 tb.addField( new Roo.form.ComboBox({
27940                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27941                         id : 'val',
27942                         fields: ['val', 'display'],
27943                         data : opts  
27944                     }),
27945                     name : '-roo-edit-' + i,
27946                     attrname : i,
27947                     stylename : item.style ? item.style : false,
27948                     displayField: item.displayField ? item.displayField : 'val',
27949                     valueField :  'val',
27950                     typeAhead: false,
27951                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27952                     editable : false,
27953                     triggerAction: 'all',
27954                     emptyText:'Select',
27955                     selectOnFocus:true,
27956                     width: item.width ? item.width  : 130,
27957                     listeners : {
27958                         'select': function(c, r, i) {
27959                             if (c.stylename) {
27960                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27961                                 return;
27962                             }
27963                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27964                         }
27965                     }
27966
27967                 }));
27968                 continue;
27969                     
27970                  
27971                 
27972                 tb.addField( new Roo.form.TextField({
27973                     name: i,
27974                     width: 100,
27975                     //allowBlank:false,
27976                     value: ''
27977                 }));
27978                 continue;
27979             }
27980             tb.addField( new Roo.form.TextField({
27981                 name: '-roo-edit-' + i,
27982                 attrname : i,
27983                 
27984                 width: item.width,
27985                 //allowBlank:true,
27986                 value: '',
27987                 listeners: {
27988                     'change' : function(f, nv, ov) {
27989                         tb.selectedNode.setAttribute(f.attrname, nv);
27990                     }
27991                 }
27992             }));
27993              
27994         }
27995         tb.addFill();
27996         var _this = this;
27997         tb.addButton( {
27998             text: 'Remove Tag',
27999     
28000             listeners : {
28001                 click : function ()
28002                 {
28003                     // remove
28004                     // undo does not work.
28005                      
28006                     var sn = tb.selectedNode;
28007                     
28008                     var pn = sn.parentNode;
28009                     
28010                     var stn =  sn.childNodes[0];
28011                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28012                     while (sn.childNodes.length) {
28013                         var node = sn.childNodes[0];
28014                         sn.removeChild(node);
28015                         //Roo.log(node);
28016                         pn.insertBefore(node, sn);
28017                         
28018                     }
28019                     pn.removeChild(sn);
28020                     var range = editorcore.createRange();
28021         
28022                     range.setStart(stn,0);
28023                     range.setEnd(en,0); //????
28024                     //range.selectNode(sel);
28025                     
28026                     
28027                     var selection = editorcore.getSelection();
28028                     selection.removeAllRanges();
28029                     selection.addRange(range);
28030                     
28031                     
28032                     
28033                     //_this.updateToolbar(null, null, pn);
28034                     _this.updateToolbar(null, null, null);
28035                     _this.footDisp.dom.innerHTML = ''; 
28036                 }
28037             }
28038             
28039                     
28040                 
28041             
28042         });
28043         
28044         
28045         tb.el.on('click', function(e){
28046             e.preventDefault(); // what does this do?
28047         });
28048         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28049         tb.el.hide();
28050         tb.name = nm;
28051         // dont need to disable them... as they will get hidden
28052         return tb;
28053          
28054         
28055     },
28056     buildFooter : function()
28057     {
28058         
28059         var fel = this.editor.wrap.createChild();
28060         this.footer = new Roo.Toolbar(fel);
28061         // toolbar has scrolly on left / right?
28062         var footDisp= new Roo.Toolbar.Fill();
28063         var _t = this;
28064         this.footer.add(
28065             {
28066                 text : '&lt;',
28067                 xtype: 'Button',
28068                 handler : function() {
28069                     _t.footDisp.scrollTo('left',0,true)
28070                 }
28071             }
28072         );
28073         this.footer.add( footDisp );
28074         this.footer.add( 
28075             {
28076                 text : '&gt;',
28077                 xtype: 'Button',
28078                 handler : function() {
28079                     // no animation..
28080                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28081                 }
28082             }
28083         );
28084         var fel = Roo.get(footDisp.el);
28085         fel.addClass('x-editor-context');
28086         this.footDispWrap = fel; 
28087         this.footDispWrap.overflow  = 'hidden';
28088         
28089         this.footDisp = fel.createChild();
28090         this.footDispWrap.on('click', this.onContextClick, this)
28091         
28092         
28093     },
28094     onContextClick : function (ev,dom)
28095     {
28096         ev.preventDefault();
28097         var  cn = dom.className;
28098         //Roo.log(cn);
28099         if (!cn.match(/x-ed-loc-/)) {
28100             return;
28101         }
28102         var n = cn.split('-').pop();
28103         var ans = this.footerEls;
28104         var sel = ans[n];
28105         
28106          // pick
28107         var range = this.editorcore.createRange();
28108         
28109         range.selectNodeContents(sel);
28110         //range.selectNode(sel);
28111         
28112         
28113         var selection = this.editorcore.getSelection();
28114         selection.removeAllRanges();
28115         selection.addRange(range);
28116         
28117         
28118         
28119         this.updateToolbar(null, null, sel);
28120         
28121         
28122     }
28123     
28124     
28125     
28126     
28127     
28128 });
28129
28130
28131
28132
28133
28134 /*
28135  * Based on:
28136  * Ext JS Library 1.1.1
28137  * Copyright(c) 2006-2007, Ext JS, LLC.
28138  *
28139  * Originally Released Under LGPL - original licence link has changed is not relivant.
28140  *
28141  * Fork - LGPL
28142  * <script type="text/javascript">
28143  */
28144  
28145 /**
28146  * @class Roo.form.BasicForm
28147  * @extends Roo.util.Observable
28148  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28149  * @constructor
28150  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28151  * @param {Object} config Configuration options
28152  */
28153 Roo.form.BasicForm = function(el, config){
28154     this.allItems = [];
28155     this.childForms = [];
28156     Roo.apply(this, config);
28157     /*
28158      * The Roo.form.Field items in this form.
28159      * @type MixedCollection
28160      */
28161      
28162      
28163     this.items = new Roo.util.MixedCollection(false, function(o){
28164         return o.id || (o.id = Roo.id());
28165     });
28166     this.addEvents({
28167         /**
28168          * @event beforeaction
28169          * Fires before any action is performed. Return false to cancel the action.
28170          * @param {Form} this
28171          * @param {Action} action The action to be performed
28172          */
28173         beforeaction: true,
28174         /**
28175          * @event actionfailed
28176          * Fires when an action fails.
28177          * @param {Form} this
28178          * @param {Action} action The action that failed
28179          */
28180         actionfailed : true,
28181         /**
28182          * @event actioncomplete
28183          * Fires when an action is completed.
28184          * @param {Form} this
28185          * @param {Action} action The action that completed
28186          */
28187         actioncomplete : true
28188     });
28189     if(el){
28190         this.initEl(el);
28191     }
28192     Roo.form.BasicForm.superclass.constructor.call(this);
28193 };
28194
28195 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28196     /**
28197      * @cfg {String} method
28198      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28199      */
28200     /**
28201      * @cfg {DataReader} reader
28202      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28203      * This is optional as there is built-in support for processing JSON.
28204      */
28205     /**
28206      * @cfg {DataReader} errorReader
28207      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28208      * This is completely optional as there is built-in support for processing JSON.
28209      */
28210     /**
28211      * @cfg {String} url
28212      * The URL to use for form actions if one isn't supplied in the action options.
28213      */
28214     /**
28215      * @cfg {Boolean} fileUpload
28216      * Set to true if this form is a file upload.
28217      */
28218      
28219     /**
28220      * @cfg {Object} baseParams
28221      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28222      */
28223      /**
28224      
28225     /**
28226      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28227      */
28228     timeout: 30,
28229
28230     // private
28231     activeAction : null,
28232
28233     /**
28234      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28235      * or setValues() data instead of when the form was first created.
28236      */
28237     trackResetOnLoad : false,
28238     
28239     
28240     /**
28241      * childForms - used for multi-tab forms
28242      * @type {Array}
28243      */
28244     childForms : false,
28245     
28246     /**
28247      * allItems - full list of fields.
28248      * @type {Array}
28249      */
28250     allItems : false,
28251     
28252     /**
28253      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28254      * element by passing it or its id or mask the form itself by passing in true.
28255      * @type Mixed
28256      */
28257     waitMsgTarget : false,
28258
28259     // private
28260     initEl : function(el){
28261         this.el = Roo.get(el);
28262         this.id = this.el.id || Roo.id();
28263         this.el.on('submit', this.onSubmit, this);
28264         this.el.addClass('x-form');
28265     },
28266
28267     // private
28268     onSubmit : function(e){
28269         e.stopEvent();
28270     },
28271
28272     /**
28273      * Returns true if client-side validation on the form is successful.
28274      * @return Boolean
28275      */
28276     isValid : function(){
28277         var valid = true;
28278         this.items.each(function(f){
28279            if(!f.validate()){
28280                valid = false;
28281            }
28282         });
28283         return valid;
28284     },
28285
28286     /**
28287      * Returns true if any fields in this form have changed since their original load.
28288      * @return Boolean
28289      */
28290     isDirty : function(){
28291         var dirty = false;
28292         this.items.each(function(f){
28293            if(f.isDirty()){
28294                dirty = true;
28295                return false;
28296            }
28297         });
28298         return dirty;
28299     },
28300
28301     /**
28302      * Performs a predefined action (submit or load) or custom actions you define on this form.
28303      * @param {String} actionName The name of the action type
28304      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28305      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28306      * accept other config options):
28307      * <pre>
28308 Property          Type             Description
28309 ----------------  ---------------  ----------------------------------------------------------------------------------
28310 url               String           The url for the action (defaults to the form's url)
28311 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28312 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28313 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28314                                    validate the form on the client (defaults to false)
28315      * </pre>
28316      * @return {BasicForm} this
28317      */
28318     doAction : function(action, options){
28319         if(typeof action == 'string'){
28320             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28321         }
28322         if(this.fireEvent('beforeaction', this, action) !== false){
28323             this.beforeAction(action);
28324             action.run.defer(100, action);
28325         }
28326         return this;
28327     },
28328
28329     /**
28330      * Shortcut to do a submit action.
28331      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28332      * @return {BasicForm} this
28333      */
28334     submit : function(options){
28335         this.doAction('submit', options);
28336         return this;
28337     },
28338
28339     /**
28340      * Shortcut to do a load action.
28341      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28342      * @return {BasicForm} this
28343      */
28344     load : function(options){
28345         this.doAction('load', options);
28346         return this;
28347     },
28348
28349     /**
28350      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28351      * @param {Record} record The record to edit
28352      * @return {BasicForm} this
28353      */
28354     updateRecord : function(record){
28355         record.beginEdit();
28356         var fs = record.fields;
28357         fs.each(function(f){
28358             var field = this.findField(f.name);
28359             if(field){
28360                 record.set(f.name, field.getValue());
28361             }
28362         }, this);
28363         record.endEdit();
28364         return this;
28365     },
28366
28367     /**
28368      * Loads an Roo.data.Record into this form.
28369      * @param {Record} record The record to load
28370      * @return {BasicForm} this
28371      */
28372     loadRecord : function(record){
28373         this.setValues(record.data);
28374         return this;
28375     },
28376
28377     // private
28378     beforeAction : function(action){
28379         var o = action.options;
28380         
28381        
28382         if(this.waitMsgTarget === true){
28383             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28384         }else if(this.waitMsgTarget){
28385             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28386             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28387         }else {
28388             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28389         }
28390          
28391     },
28392
28393     // private
28394     afterAction : function(action, success){
28395         this.activeAction = null;
28396         var o = action.options;
28397         
28398         if(this.waitMsgTarget === true){
28399             this.el.unmask();
28400         }else if(this.waitMsgTarget){
28401             this.waitMsgTarget.unmask();
28402         }else{
28403             Roo.MessageBox.updateProgress(1);
28404             Roo.MessageBox.hide();
28405         }
28406          
28407         if(success){
28408             if(o.reset){
28409                 this.reset();
28410             }
28411             Roo.callback(o.success, o.scope, [this, action]);
28412             this.fireEvent('actioncomplete', this, action);
28413             
28414         }else{
28415             
28416             // failure condition..
28417             // we have a scenario where updates need confirming.
28418             // eg. if a locking scenario exists..
28419             // we look for { errors : { needs_confirm : true }} in the response.
28420             if (
28421                 (typeof(action.result) != 'undefined')  &&
28422                 (typeof(action.result.errors) != 'undefined')  &&
28423                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28424            ){
28425                 var _t = this;
28426                 Roo.MessageBox.confirm(
28427                     "Change requires confirmation",
28428                     action.result.errorMsg,
28429                     function(r) {
28430                         if (r != 'yes') {
28431                             return;
28432                         }
28433                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28434                     }
28435                     
28436                 );
28437                 
28438                 
28439                 
28440                 return;
28441             }
28442             
28443             Roo.callback(o.failure, o.scope, [this, action]);
28444             // show an error message if no failed handler is set..
28445             if (!this.hasListener('actionfailed')) {
28446                 Roo.MessageBox.alert("Error",
28447                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28448                         action.result.errorMsg :
28449                         "Saving Failed, please check your entries or try again"
28450                 );
28451             }
28452             
28453             this.fireEvent('actionfailed', this, action);
28454         }
28455         
28456     },
28457
28458     /**
28459      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28460      * @param {String} id The value to search for
28461      * @return Field
28462      */
28463     findField : function(id){
28464         var field = this.items.get(id);
28465         if(!field){
28466             this.items.each(function(f){
28467                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28468                     field = f;
28469                     return false;
28470                 }
28471             });
28472         }
28473         return field || null;
28474     },
28475
28476     /**
28477      * Add a secondary form to this one, 
28478      * Used to provide tabbed forms. One form is primary, with hidden values 
28479      * which mirror the elements from the other forms.
28480      * 
28481      * @param {Roo.form.Form} form to add.
28482      * 
28483      */
28484     addForm : function(form)
28485     {
28486        
28487         if (this.childForms.indexOf(form) > -1) {
28488             // already added..
28489             return;
28490         }
28491         this.childForms.push(form);
28492         var n = '';
28493         Roo.each(form.allItems, function (fe) {
28494             
28495             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28496             if (this.findField(n)) { // already added..
28497                 return;
28498             }
28499             var add = new Roo.form.Hidden({
28500                 name : n
28501             });
28502             add.render(this.el);
28503             
28504             this.add( add );
28505         }, this);
28506         
28507     },
28508     /**
28509      * Mark fields in this form invalid in bulk.
28510      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28511      * @return {BasicForm} this
28512      */
28513     markInvalid : function(errors){
28514         if(errors instanceof Array){
28515             for(var i = 0, len = errors.length; i < len; i++){
28516                 var fieldError = errors[i];
28517                 var f = this.findField(fieldError.id);
28518                 if(f){
28519                     f.markInvalid(fieldError.msg);
28520                 }
28521             }
28522         }else{
28523             var field, id;
28524             for(id in errors){
28525                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28526                     field.markInvalid(errors[id]);
28527                 }
28528             }
28529         }
28530         Roo.each(this.childForms || [], function (f) {
28531             f.markInvalid(errors);
28532         });
28533         
28534         return this;
28535     },
28536
28537     /**
28538      * Set values for fields in this form in bulk.
28539      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28540      * @return {BasicForm} this
28541      */
28542     setValues : function(values){
28543         if(values instanceof Array){ // array of objects
28544             for(var i = 0, len = values.length; i < len; i++){
28545                 var v = values[i];
28546                 var f = this.findField(v.id);
28547                 if(f){
28548                     f.setValue(v.value);
28549                     if(this.trackResetOnLoad){
28550                         f.originalValue = f.getValue();
28551                     }
28552                 }
28553             }
28554         }else{ // object hash
28555             var field, id;
28556             for(id in values){
28557                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28558                     
28559                     if (field.setFromData && 
28560                         field.valueField && 
28561                         field.displayField &&
28562                         // combos' with local stores can 
28563                         // be queried via setValue()
28564                         // to set their value..
28565                         (field.store && !field.store.isLocal)
28566                         ) {
28567                         // it's a combo
28568                         var sd = { };
28569                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28570                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28571                         field.setFromData(sd);
28572                         
28573                     } else {
28574                         field.setValue(values[id]);
28575                     }
28576                     
28577                     
28578                     if(this.trackResetOnLoad){
28579                         field.originalValue = field.getValue();
28580                     }
28581                 }
28582             }
28583         }
28584          
28585         Roo.each(this.childForms || [], function (f) {
28586             f.setValues(values);
28587         });
28588                 
28589         return this;
28590     },
28591
28592     /**
28593      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28594      * they are returned as an array.
28595      * @param {Boolean} asString
28596      * @return {Object}
28597      */
28598     getValues : function(asString){
28599         if (this.childForms) {
28600             // copy values from the child forms
28601             Roo.each(this.childForms, function (f) {
28602                 this.setValues(f.getValues());
28603             }, this);
28604         }
28605         
28606         
28607         
28608         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28609         if(asString === true){
28610             return fs;
28611         }
28612         return Roo.urlDecode(fs);
28613     },
28614     
28615     /**
28616      * Returns the fields in this form as an object with key/value pairs. 
28617      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28618      * @return {Object}
28619      */
28620     getFieldValues : function(with_hidden)
28621     {
28622         if (this.childForms) {
28623             // copy values from the child forms
28624             // should this call getFieldValues - probably not as we do not currently copy
28625             // hidden fields when we generate..
28626             Roo.each(this.childForms, function (f) {
28627                 this.setValues(f.getValues());
28628             }, this);
28629         }
28630         
28631         var ret = {};
28632         this.items.each(function(f){
28633             if (!f.getName()) {
28634                 return;
28635             }
28636             var v = f.getValue();
28637             if (f.inputType =='radio') {
28638                 if (typeof(ret[f.getName()]) == 'undefined') {
28639                     ret[f.getName()] = ''; // empty..
28640                 }
28641                 
28642                 if (!f.el.dom.checked) {
28643                     return;
28644                     
28645                 }
28646                 v = f.el.dom.value;
28647                 
28648             }
28649             
28650             // not sure if this supported any more..
28651             if ((typeof(v) == 'object') && f.getRawValue) {
28652                 v = f.getRawValue() ; // dates..
28653             }
28654             // combo boxes where name != hiddenName...
28655             if (f.name != f.getName()) {
28656                 ret[f.name] = f.getRawValue();
28657             }
28658             ret[f.getName()] = v;
28659         });
28660         
28661         return ret;
28662     },
28663
28664     /**
28665      * Clears all invalid messages in this form.
28666      * @return {BasicForm} this
28667      */
28668     clearInvalid : function(){
28669         this.items.each(function(f){
28670            f.clearInvalid();
28671         });
28672         
28673         Roo.each(this.childForms || [], function (f) {
28674             f.clearInvalid();
28675         });
28676         
28677         
28678         return this;
28679     },
28680
28681     /**
28682      * Resets this form.
28683      * @return {BasicForm} this
28684      */
28685     reset : function(){
28686         this.items.each(function(f){
28687             f.reset();
28688         });
28689         
28690         Roo.each(this.childForms || [], function (f) {
28691             f.reset();
28692         });
28693        
28694         
28695         return this;
28696     },
28697
28698     /**
28699      * Add Roo.form components to this form.
28700      * @param {Field} field1
28701      * @param {Field} field2 (optional)
28702      * @param {Field} etc (optional)
28703      * @return {BasicForm} this
28704      */
28705     add : function(){
28706         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28707         return this;
28708     },
28709
28710
28711     /**
28712      * Removes a field from the items collection (does NOT remove its markup).
28713      * @param {Field} field
28714      * @return {BasicForm} this
28715      */
28716     remove : function(field){
28717         this.items.remove(field);
28718         return this;
28719     },
28720
28721     /**
28722      * Looks at the fields in this form, checks them for an id attribute,
28723      * and calls applyTo on the existing dom element with that id.
28724      * @return {BasicForm} this
28725      */
28726     render : function(){
28727         this.items.each(function(f){
28728             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28729                 f.applyTo(f.id);
28730             }
28731         });
28732         return this;
28733     },
28734
28735     /**
28736      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28737      * @param {Object} values
28738      * @return {BasicForm} this
28739      */
28740     applyToFields : function(o){
28741         this.items.each(function(f){
28742            Roo.apply(f, o);
28743         });
28744         return this;
28745     },
28746
28747     /**
28748      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28749      * @param {Object} values
28750      * @return {BasicForm} this
28751      */
28752     applyIfToFields : function(o){
28753         this.items.each(function(f){
28754            Roo.applyIf(f, o);
28755         });
28756         return this;
28757     }
28758 });
28759
28760 // back compat
28761 Roo.BasicForm = Roo.form.BasicForm;/*
28762  * Based on:
28763  * Ext JS Library 1.1.1
28764  * Copyright(c) 2006-2007, Ext JS, LLC.
28765  *
28766  * Originally Released Under LGPL - original licence link has changed is not relivant.
28767  *
28768  * Fork - LGPL
28769  * <script type="text/javascript">
28770  */
28771
28772 /**
28773  * @class Roo.form.Form
28774  * @extends Roo.form.BasicForm
28775  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28776  * @constructor
28777  * @param {Object} config Configuration options
28778  */
28779 Roo.form.Form = function(config){
28780     var xitems =  [];
28781     if (config.items) {
28782         xitems = config.items;
28783         delete config.items;
28784     }
28785    
28786     
28787     Roo.form.Form.superclass.constructor.call(this, null, config);
28788     this.url = this.url || this.action;
28789     if(!this.root){
28790         this.root = new Roo.form.Layout(Roo.applyIf({
28791             id: Roo.id()
28792         }, config));
28793     }
28794     this.active = this.root;
28795     /**
28796      * Array of all the buttons that have been added to this form via {@link addButton}
28797      * @type Array
28798      */
28799     this.buttons = [];
28800     this.allItems = [];
28801     this.addEvents({
28802         /**
28803          * @event clientvalidation
28804          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28805          * @param {Form} this
28806          * @param {Boolean} valid true if the form has passed client-side validation
28807          */
28808         clientvalidation: true,
28809         /**
28810          * @event rendered
28811          * Fires when the form is rendered
28812          * @param {Roo.form.Form} form
28813          */
28814         rendered : true
28815     });
28816     
28817     if (this.progressUrl) {
28818             // push a hidden field onto the list of fields..
28819             this.addxtype( {
28820                     xns: Roo.form, 
28821                     xtype : 'Hidden', 
28822                     name : 'UPLOAD_IDENTIFIER' 
28823             });
28824         }
28825         
28826     
28827     Roo.each(xitems, this.addxtype, this);
28828     
28829     
28830     
28831 };
28832
28833 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28834     /**
28835      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28836      */
28837     /**
28838      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28839      */
28840     /**
28841      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28842      */
28843     buttonAlign:'center',
28844
28845     /**
28846      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28847      */
28848     minButtonWidth:75,
28849
28850     /**
28851      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28852      * This property cascades to child containers if not set.
28853      */
28854     labelAlign:'left',
28855
28856     /**
28857      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28858      * fires a looping event with that state. This is required to bind buttons to the valid
28859      * state using the config value formBind:true on the button.
28860      */
28861     monitorValid : false,
28862
28863     /**
28864      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28865      */
28866     monitorPoll : 200,
28867     
28868     /**
28869      * @cfg {String} progressUrl - Url to return progress data 
28870      */
28871     
28872     progressUrl : false,
28873   
28874     /**
28875      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28876      * fields are added and the column is closed. If no fields are passed the column remains open
28877      * until end() is called.
28878      * @param {Object} config The config to pass to the column
28879      * @param {Field} field1 (optional)
28880      * @param {Field} field2 (optional)
28881      * @param {Field} etc (optional)
28882      * @return Column The column container object
28883      */
28884     column : function(c){
28885         var col = new Roo.form.Column(c);
28886         this.start(col);
28887         if(arguments.length > 1){ // duplicate code required because of Opera
28888             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28889             this.end();
28890         }
28891         return col;
28892     },
28893
28894     /**
28895      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28896      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28897      * until end() is called.
28898      * @param {Object} config The config to pass to the fieldset
28899      * @param {Field} field1 (optional)
28900      * @param {Field} field2 (optional)
28901      * @param {Field} etc (optional)
28902      * @return FieldSet The fieldset container object
28903      */
28904     fieldset : function(c){
28905         var fs = new Roo.form.FieldSet(c);
28906         this.start(fs);
28907         if(arguments.length > 1){ // duplicate code required because of Opera
28908             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28909             this.end();
28910         }
28911         return fs;
28912     },
28913
28914     /**
28915      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28916      * fields are added and the container is closed. If no fields are passed the container remains open
28917      * until end() is called.
28918      * @param {Object} config The config to pass to the Layout
28919      * @param {Field} field1 (optional)
28920      * @param {Field} field2 (optional)
28921      * @param {Field} etc (optional)
28922      * @return Layout The container object
28923      */
28924     container : function(c){
28925         var l = new Roo.form.Layout(c);
28926         this.start(l);
28927         if(arguments.length > 1){ // duplicate code required because of Opera
28928             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28929             this.end();
28930         }
28931         return l;
28932     },
28933
28934     /**
28935      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28936      * @param {Object} container A Roo.form.Layout or subclass of Layout
28937      * @return {Form} this
28938      */
28939     start : function(c){
28940         // cascade label info
28941         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28942         this.active.stack.push(c);
28943         c.ownerCt = this.active;
28944         this.active = c;
28945         return this;
28946     },
28947
28948     /**
28949      * Closes the current open container
28950      * @return {Form} this
28951      */
28952     end : function(){
28953         if(this.active == this.root){
28954             return this;
28955         }
28956         this.active = this.active.ownerCt;
28957         return this;
28958     },
28959
28960     /**
28961      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28962      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28963      * as the label of the field.
28964      * @param {Field} field1
28965      * @param {Field} field2 (optional)
28966      * @param {Field} etc. (optional)
28967      * @return {Form} this
28968      */
28969     add : function(){
28970         this.active.stack.push.apply(this.active.stack, arguments);
28971         this.allItems.push.apply(this.allItems,arguments);
28972         var r = [];
28973         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28974             if(a[i].isFormField){
28975                 r.push(a[i]);
28976             }
28977         }
28978         if(r.length > 0){
28979             Roo.form.Form.superclass.add.apply(this, r);
28980         }
28981         return this;
28982     },
28983     
28984
28985     
28986     
28987     
28988      /**
28989      * Find any element that has been added to a form, using it's ID or name
28990      * This can include framesets, columns etc. along with regular fields..
28991      * @param {String} id - id or name to find.
28992      
28993      * @return {Element} e - or false if nothing found.
28994      */
28995     findbyId : function(id)
28996     {
28997         var ret = false;
28998         if (!id) {
28999             return ret;
29000         }
29001         Roo.each(this.allItems, function(f){
29002             if (f.id == id || f.name == id ){
29003                 ret = f;
29004                 return false;
29005             }
29006         });
29007         return ret;
29008     },
29009
29010     
29011     
29012     /**
29013      * Render this form into the passed container. This should only be called once!
29014      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29015      * @return {Form} this
29016      */
29017     render : function(ct)
29018     {
29019         
29020         
29021         
29022         ct = Roo.get(ct);
29023         var o = this.autoCreate || {
29024             tag: 'form',
29025             method : this.method || 'POST',
29026             id : this.id || Roo.id()
29027         };
29028         this.initEl(ct.createChild(o));
29029
29030         this.root.render(this.el);
29031         
29032        
29033              
29034         this.items.each(function(f){
29035             f.render('x-form-el-'+f.id);
29036         });
29037
29038         if(this.buttons.length > 0){
29039             // tables are required to maintain order and for correct IE layout
29040             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29041                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29042                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29043             }}, null, true);
29044             var tr = tb.getElementsByTagName('tr')[0];
29045             for(var i = 0, len = this.buttons.length; i < len; i++) {
29046                 var b = this.buttons[i];
29047                 var td = document.createElement('td');
29048                 td.className = 'x-form-btn-td';
29049                 b.render(tr.appendChild(td));
29050             }
29051         }
29052         if(this.monitorValid){ // initialize after render
29053             this.startMonitoring();
29054         }
29055         this.fireEvent('rendered', this);
29056         return this;
29057     },
29058
29059     /**
29060      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29061      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29062      * object or a valid Roo.DomHelper element config
29063      * @param {Function} handler The function called when the button is clicked
29064      * @param {Object} scope (optional) The scope of the handler function
29065      * @return {Roo.Button}
29066      */
29067     addButton : function(config, handler, scope){
29068         var bc = {
29069             handler: handler,
29070             scope: scope,
29071             minWidth: this.minButtonWidth,
29072             hideParent:true
29073         };
29074         if(typeof config == "string"){
29075             bc.text = config;
29076         }else{
29077             Roo.apply(bc, config);
29078         }
29079         var btn = new Roo.Button(null, bc);
29080         this.buttons.push(btn);
29081         return btn;
29082     },
29083
29084      /**
29085      * Adds a series of form elements (using the xtype property as the factory method.
29086      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29087      * @param {Object} config 
29088      */
29089     
29090     addxtype : function()
29091     {
29092         var ar = Array.prototype.slice.call(arguments, 0);
29093         var ret = false;
29094         for(var i = 0; i < ar.length; i++) {
29095             if (!ar[i]) {
29096                 continue; // skip -- if this happends something invalid got sent, we 
29097                 // should ignore it, as basically that interface element will not show up
29098                 // and that should be pretty obvious!!
29099             }
29100             
29101             if (Roo.form[ar[i].xtype]) {
29102                 ar[i].form = this;
29103                 var fe = Roo.factory(ar[i], Roo.form);
29104                 if (!ret) {
29105                     ret = fe;
29106                 }
29107                 fe.form = this;
29108                 if (fe.store) {
29109                     fe.store.form = this;
29110                 }
29111                 if (fe.isLayout) {  
29112                          
29113                     this.start(fe);
29114                     this.allItems.push(fe);
29115                     if (fe.items && fe.addxtype) {
29116                         fe.addxtype.apply(fe, fe.items);
29117                         delete fe.items;
29118                     }
29119                      this.end();
29120                     continue;
29121                 }
29122                 
29123                 
29124                  
29125                 this.add(fe);
29126               //  console.log('adding ' + ar[i].xtype);
29127             }
29128             if (ar[i].xtype == 'Button') {  
29129                 //console.log('adding button');
29130                 //console.log(ar[i]);
29131                 this.addButton(ar[i]);
29132                 this.allItems.push(fe);
29133                 continue;
29134             }
29135             
29136             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29137                 alert('end is not supported on xtype any more, use items');
29138             //    this.end();
29139             //    //console.log('adding end');
29140             }
29141             
29142         }
29143         return ret;
29144     },
29145     
29146     /**
29147      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29148      * option "monitorValid"
29149      */
29150     startMonitoring : function(){
29151         if(!this.bound){
29152             this.bound = true;
29153             Roo.TaskMgr.start({
29154                 run : this.bindHandler,
29155                 interval : this.monitorPoll || 200,
29156                 scope: this
29157             });
29158         }
29159     },
29160
29161     /**
29162      * Stops monitoring of the valid state of this form
29163      */
29164     stopMonitoring : function(){
29165         this.bound = false;
29166     },
29167
29168     // private
29169     bindHandler : function(){
29170         if(!this.bound){
29171             return false; // stops binding
29172         }
29173         var valid = true;
29174         this.items.each(function(f){
29175             if(!f.isValid(true)){
29176                 valid = false;
29177                 return false;
29178             }
29179         });
29180         for(var i = 0, len = this.buttons.length; i < len; i++){
29181             var btn = this.buttons[i];
29182             if(btn.formBind === true && btn.disabled === valid){
29183                 btn.setDisabled(!valid);
29184             }
29185         }
29186         this.fireEvent('clientvalidation', this, valid);
29187     }
29188     
29189     
29190     
29191     
29192     
29193     
29194     
29195     
29196 });
29197
29198
29199 // back compat
29200 Roo.Form = Roo.form.Form;
29201 /*
29202  * Based on:
29203  * Ext JS Library 1.1.1
29204  * Copyright(c) 2006-2007, Ext JS, LLC.
29205  *
29206  * Originally Released Under LGPL - original licence link has changed is not relivant.
29207  *
29208  * Fork - LGPL
29209  * <script type="text/javascript">
29210  */
29211
29212 // as we use this in bootstrap.
29213 Roo.namespace('Roo.form');
29214  /**
29215  * @class Roo.form.Action
29216  * Internal Class used to handle form actions
29217  * @constructor
29218  * @param {Roo.form.BasicForm} el The form element or its id
29219  * @param {Object} config Configuration options
29220  */
29221
29222  
29223  
29224 // define the action interface
29225 Roo.form.Action = function(form, options){
29226     this.form = form;
29227     this.options = options || {};
29228 };
29229 /**
29230  * Client Validation Failed
29231  * @const 
29232  */
29233 Roo.form.Action.CLIENT_INVALID = 'client';
29234 /**
29235  * Server Validation Failed
29236  * @const 
29237  */
29238 Roo.form.Action.SERVER_INVALID = 'server';
29239  /**
29240  * Connect to Server Failed
29241  * @const 
29242  */
29243 Roo.form.Action.CONNECT_FAILURE = 'connect';
29244 /**
29245  * Reading Data from Server Failed
29246  * @const 
29247  */
29248 Roo.form.Action.LOAD_FAILURE = 'load';
29249
29250 Roo.form.Action.prototype = {
29251     type : 'default',
29252     failureType : undefined,
29253     response : undefined,
29254     result : undefined,
29255
29256     // interface method
29257     run : function(options){
29258
29259     },
29260
29261     // interface method
29262     success : function(response){
29263
29264     },
29265
29266     // interface method
29267     handleResponse : function(response){
29268
29269     },
29270
29271     // default connection failure
29272     failure : function(response){
29273         
29274         this.response = response;
29275         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29276         this.form.afterAction(this, false);
29277     },
29278
29279     processResponse : function(response){
29280         this.response = response;
29281         if(!response.responseText){
29282             return true;
29283         }
29284         this.result = this.handleResponse(response);
29285         return this.result;
29286     },
29287
29288     // utility functions used internally
29289     getUrl : function(appendParams){
29290         var url = this.options.url || this.form.url || this.form.el.dom.action;
29291         if(appendParams){
29292             var p = this.getParams();
29293             if(p){
29294                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29295             }
29296         }
29297         return url;
29298     },
29299
29300     getMethod : function(){
29301         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29302     },
29303
29304     getParams : function(){
29305         var bp = this.form.baseParams;
29306         var p = this.options.params;
29307         if(p){
29308             if(typeof p == "object"){
29309                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29310             }else if(typeof p == 'string' && bp){
29311                 p += '&' + Roo.urlEncode(bp);
29312             }
29313         }else if(bp){
29314             p = Roo.urlEncode(bp);
29315         }
29316         return p;
29317     },
29318
29319     createCallback : function(){
29320         return {
29321             success: this.success,
29322             failure: this.failure,
29323             scope: this,
29324             timeout: (this.form.timeout*1000),
29325             upload: this.form.fileUpload ? this.success : undefined
29326         };
29327     }
29328 };
29329
29330 Roo.form.Action.Submit = function(form, options){
29331     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29332 };
29333
29334 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29335     type : 'submit',
29336
29337     haveProgress : false,
29338     uploadComplete : false,
29339     
29340     // uploadProgress indicator.
29341     uploadProgress : function()
29342     {
29343         if (!this.form.progressUrl) {
29344             return;
29345         }
29346         
29347         if (!this.haveProgress) {
29348             Roo.MessageBox.progress("Uploading", "Uploading");
29349         }
29350         if (this.uploadComplete) {
29351            Roo.MessageBox.hide();
29352            return;
29353         }
29354         
29355         this.haveProgress = true;
29356    
29357         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29358         
29359         var c = new Roo.data.Connection();
29360         c.request({
29361             url : this.form.progressUrl,
29362             params: {
29363                 id : uid
29364             },
29365             method: 'GET',
29366             success : function(req){
29367                //console.log(data);
29368                 var rdata = false;
29369                 var edata;
29370                 try  {
29371                    rdata = Roo.decode(req.responseText)
29372                 } catch (e) {
29373                     Roo.log("Invalid data from server..");
29374                     Roo.log(edata);
29375                     return;
29376                 }
29377                 if (!rdata || !rdata.success) {
29378                     Roo.log(rdata);
29379                     Roo.MessageBox.alert(Roo.encode(rdata));
29380                     return;
29381                 }
29382                 var data = rdata.data;
29383                 
29384                 if (this.uploadComplete) {
29385                    Roo.MessageBox.hide();
29386                    return;
29387                 }
29388                    
29389                 if (data){
29390                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29391                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29392                     );
29393                 }
29394                 this.uploadProgress.defer(2000,this);
29395             },
29396        
29397             failure: function(data) {
29398                 Roo.log('progress url failed ');
29399                 Roo.log(data);
29400             },
29401             scope : this
29402         });
29403            
29404     },
29405     
29406     
29407     run : function()
29408     {
29409         // run get Values on the form, so it syncs any secondary forms.
29410         this.form.getValues();
29411         
29412         var o = this.options;
29413         var method = this.getMethod();
29414         var isPost = method == 'POST';
29415         if(o.clientValidation === false || this.form.isValid()){
29416             
29417             if (this.form.progressUrl) {
29418                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29419                     (new Date() * 1) + '' + Math.random());
29420                     
29421             } 
29422             
29423             
29424             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29425                 form:this.form.el.dom,
29426                 url:this.getUrl(!isPost),
29427                 method: method,
29428                 params:isPost ? this.getParams() : null,
29429                 isUpload: this.form.fileUpload
29430             }));
29431             
29432             this.uploadProgress();
29433
29434         }else if (o.clientValidation !== false){ // client validation failed
29435             this.failureType = Roo.form.Action.CLIENT_INVALID;
29436             this.form.afterAction(this, false);
29437         }
29438     },
29439
29440     success : function(response)
29441     {
29442         this.uploadComplete= true;
29443         if (this.haveProgress) {
29444             Roo.MessageBox.hide();
29445         }
29446         
29447         
29448         var result = this.processResponse(response);
29449         if(result === true || result.success){
29450             this.form.afterAction(this, true);
29451             return;
29452         }
29453         if(result.errors){
29454             this.form.markInvalid(result.errors);
29455             this.failureType = Roo.form.Action.SERVER_INVALID;
29456         }
29457         this.form.afterAction(this, false);
29458     },
29459     failure : function(response)
29460     {
29461         this.uploadComplete= true;
29462         if (this.haveProgress) {
29463             Roo.MessageBox.hide();
29464         }
29465         
29466         this.response = response;
29467         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29468         this.form.afterAction(this, false);
29469     },
29470     
29471     handleResponse : function(response){
29472         if(this.form.errorReader){
29473             var rs = this.form.errorReader.read(response);
29474             var errors = [];
29475             if(rs.records){
29476                 for(var i = 0, len = rs.records.length; i < len; i++) {
29477                     var r = rs.records[i];
29478                     errors[i] = r.data;
29479                 }
29480             }
29481             if(errors.length < 1){
29482                 errors = null;
29483             }
29484             return {
29485                 success : rs.success,
29486                 errors : errors
29487             };
29488         }
29489         var ret = false;
29490         try {
29491             ret = Roo.decode(response.responseText);
29492         } catch (e) {
29493             ret = {
29494                 success: false,
29495                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29496                 errors : []
29497             };
29498         }
29499         return ret;
29500         
29501     }
29502 });
29503
29504
29505 Roo.form.Action.Load = function(form, options){
29506     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29507     this.reader = this.form.reader;
29508 };
29509
29510 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29511     type : 'load',
29512
29513     run : function(){
29514         
29515         Roo.Ajax.request(Roo.apply(
29516                 this.createCallback(), {
29517                     method:this.getMethod(),
29518                     url:this.getUrl(false),
29519                     params:this.getParams()
29520         }));
29521     },
29522
29523     success : function(response){
29524         
29525         var result = this.processResponse(response);
29526         if(result === true || !result.success || !result.data){
29527             this.failureType = Roo.form.Action.LOAD_FAILURE;
29528             this.form.afterAction(this, false);
29529             return;
29530         }
29531         this.form.clearInvalid();
29532         this.form.setValues(result.data);
29533         this.form.afterAction(this, true);
29534     },
29535
29536     handleResponse : function(response){
29537         if(this.form.reader){
29538             var rs = this.form.reader.read(response);
29539             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29540             return {
29541                 success : rs.success,
29542                 data : data
29543             };
29544         }
29545         return Roo.decode(response.responseText);
29546     }
29547 });
29548
29549 Roo.form.Action.ACTION_TYPES = {
29550     'load' : Roo.form.Action.Load,
29551     'submit' : Roo.form.Action.Submit
29552 };/*
29553  * Based on:
29554  * Ext JS Library 1.1.1
29555  * Copyright(c) 2006-2007, Ext JS, LLC.
29556  *
29557  * Originally Released Under LGPL - original licence link has changed is not relivant.
29558  *
29559  * Fork - LGPL
29560  * <script type="text/javascript">
29561  */
29562  
29563 /**
29564  * @class Roo.form.Layout
29565  * @extends Roo.Component
29566  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29567  * @constructor
29568  * @param {Object} config Configuration options
29569  */
29570 Roo.form.Layout = function(config){
29571     var xitems = [];
29572     if (config.items) {
29573         xitems = config.items;
29574         delete config.items;
29575     }
29576     Roo.form.Layout.superclass.constructor.call(this, config);
29577     this.stack = [];
29578     Roo.each(xitems, this.addxtype, this);
29579      
29580 };
29581
29582 Roo.extend(Roo.form.Layout, Roo.Component, {
29583     /**
29584      * @cfg {String/Object} autoCreate
29585      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29586      */
29587     /**
29588      * @cfg {String/Object/Function} style
29589      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29590      * a function which returns such a specification.
29591      */
29592     /**
29593      * @cfg {String} labelAlign
29594      * Valid values are "left," "top" and "right" (defaults to "left")
29595      */
29596     /**
29597      * @cfg {Number} labelWidth
29598      * Fixed width in pixels of all field labels (defaults to undefined)
29599      */
29600     /**
29601      * @cfg {Boolean} clear
29602      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29603      */
29604     clear : true,
29605     /**
29606      * @cfg {String} labelSeparator
29607      * The separator to use after field labels (defaults to ':')
29608      */
29609     labelSeparator : ':',
29610     /**
29611      * @cfg {Boolean} hideLabels
29612      * True to suppress the display of field labels in this layout (defaults to false)
29613      */
29614     hideLabels : false,
29615
29616     // private
29617     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29618     
29619     isLayout : true,
29620     
29621     // private
29622     onRender : function(ct, position){
29623         if(this.el){ // from markup
29624             this.el = Roo.get(this.el);
29625         }else {  // generate
29626             var cfg = this.getAutoCreate();
29627             this.el = ct.createChild(cfg, position);
29628         }
29629         if(this.style){
29630             this.el.applyStyles(this.style);
29631         }
29632         if(this.labelAlign){
29633             this.el.addClass('x-form-label-'+this.labelAlign);
29634         }
29635         if(this.hideLabels){
29636             this.labelStyle = "display:none";
29637             this.elementStyle = "padding-left:0;";
29638         }else{
29639             if(typeof this.labelWidth == 'number'){
29640                 this.labelStyle = "width:"+this.labelWidth+"px;";
29641                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29642             }
29643             if(this.labelAlign == 'top'){
29644                 this.labelStyle = "width:auto;";
29645                 this.elementStyle = "padding-left:0;";
29646             }
29647         }
29648         var stack = this.stack;
29649         var slen = stack.length;
29650         if(slen > 0){
29651             if(!this.fieldTpl){
29652                 var t = new Roo.Template(
29653                     '<div class="x-form-item {5}">',
29654                         '<label for="{0}" style="{2}">{1}{4}</label>',
29655                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29656                         '</div>',
29657                     '</div><div class="x-form-clear-left"></div>'
29658                 );
29659                 t.disableFormats = true;
29660                 t.compile();
29661                 Roo.form.Layout.prototype.fieldTpl = t;
29662             }
29663             for(var i = 0; i < slen; i++) {
29664                 if(stack[i].isFormField){
29665                     this.renderField(stack[i]);
29666                 }else{
29667                     this.renderComponent(stack[i]);
29668                 }
29669             }
29670         }
29671         if(this.clear){
29672             this.el.createChild({cls:'x-form-clear'});
29673         }
29674     },
29675
29676     // private
29677     renderField : function(f){
29678         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29679                f.id, //0
29680                f.fieldLabel, //1
29681                f.labelStyle||this.labelStyle||'', //2
29682                this.elementStyle||'', //3
29683                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29684                f.itemCls||this.itemCls||''  //5
29685        ], true).getPrevSibling());
29686     },
29687
29688     // private
29689     renderComponent : function(c){
29690         c.render(c.isLayout ? this.el : this.el.createChild());    
29691     },
29692     /**
29693      * Adds a object form elements (using the xtype property as the factory method.)
29694      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29695      * @param {Object} config 
29696      */
29697     addxtype : function(o)
29698     {
29699         // create the lement.
29700         o.form = this.form;
29701         var fe = Roo.factory(o, Roo.form);
29702         this.form.allItems.push(fe);
29703         this.stack.push(fe);
29704         
29705         if (fe.isFormField) {
29706             this.form.items.add(fe);
29707         }
29708          
29709         return fe;
29710     }
29711 });
29712
29713 /**
29714  * @class Roo.form.Column
29715  * @extends Roo.form.Layout
29716  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29717  * @constructor
29718  * @param {Object} config Configuration options
29719  */
29720 Roo.form.Column = function(config){
29721     Roo.form.Column.superclass.constructor.call(this, config);
29722 };
29723
29724 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29725     /**
29726      * @cfg {Number/String} width
29727      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29728      */
29729     /**
29730      * @cfg {String/Object} autoCreate
29731      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29732      */
29733
29734     // private
29735     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29736
29737     // private
29738     onRender : function(ct, position){
29739         Roo.form.Column.superclass.onRender.call(this, ct, position);
29740         if(this.width){
29741             this.el.setWidth(this.width);
29742         }
29743     }
29744 });
29745
29746
29747 /**
29748  * @class Roo.form.Row
29749  * @extends Roo.form.Layout
29750  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29751  * @constructor
29752  * @param {Object} config Configuration options
29753  */
29754
29755  
29756 Roo.form.Row = function(config){
29757     Roo.form.Row.superclass.constructor.call(this, config);
29758 };
29759  
29760 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29761       /**
29762      * @cfg {Number/String} width
29763      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29764      */
29765     /**
29766      * @cfg {Number/String} height
29767      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29768      */
29769     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29770     
29771     padWidth : 20,
29772     // private
29773     onRender : function(ct, position){
29774         //console.log('row render');
29775         if(!this.rowTpl){
29776             var t = new Roo.Template(
29777                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29778                     '<label for="{0}" style="{2}">{1}{4}</label>',
29779                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29780                     '</div>',
29781                 '</div>'
29782             );
29783             t.disableFormats = true;
29784             t.compile();
29785             Roo.form.Layout.prototype.rowTpl = t;
29786         }
29787         this.fieldTpl = this.rowTpl;
29788         
29789         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29790         var labelWidth = 100;
29791         
29792         if ((this.labelAlign != 'top')) {
29793             if (typeof this.labelWidth == 'number') {
29794                 labelWidth = this.labelWidth
29795             }
29796             this.padWidth =  20 + labelWidth;
29797             
29798         }
29799         
29800         Roo.form.Column.superclass.onRender.call(this, ct, position);
29801         if(this.width){
29802             this.el.setWidth(this.width);
29803         }
29804         if(this.height){
29805             this.el.setHeight(this.height);
29806         }
29807     },
29808     
29809     // private
29810     renderField : function(f){
29811         f.fieldEl = this.fieldTpl.append(this.el, [
29812                f.id, f.fieldLabel,
29813                f.labelStyle||this.labelStyle||'',
29814                this.elementStyle||'',
29815                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29816                f.itemCls||this.itemCls||'',
29817                f.width ? f.width + this.padWidth : 160 + this.padWidth
29818        ],true);
29819     }
29820 });
29821  
29822
29823 /**
29824  * @class Roo.form.FieldSet
29825  * @extends Roo.form.Layout
29826  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29827  * @constructor
29828  * @param {Object} config Configuration options
29829  */
29830 Roo.form.FieldSet = function(config){
29831     Roo.form.FieldSet.superclass.constructor.call(this, config);
29832 };
29833
29834 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29835     /**
29836      * @cfg {String} legend
29837      * The text to display as the legend for the FieldSet (defaults to '')
29838      */
29839     /**
29840      * @cfg {String/Object} autoCreate
29841      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29842      */
29843
29844     // private
29845     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29846
29847     // private
29848     onRender : function(ct, position){
29849         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29850         if(this.legend){
29851             this.setLegend(this.legend);
29852         }
29853     },
29854
29855     // private
29856     setLegend : function(text){
29857         if(this.rendered){
29858             this.el.child('legend').update(text);
29859         }
29860     }
29861 });/*
29862  * Based on:
29863  * Ext JS Library 1.1.1
29864  * Copyright(c) 2006-2007, Ext JS, LLC.
29865  *
29866  * Originally Released Under LGPL - original licence link has changed is not relivant.
29867  *
29868  * Fork - LGPL
29869  * <script type="text/javascript">
29870  */
29871 /**
29872  * @class Roo.form.VTypes
29873  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29874  * @singleton
29875  */
29876 Roo.form.VTypes = function(){
29877     // closure these in so they are only created once.
29878     var alpha = /^[a-zA-Z_]+$/;
29879     var alphanum = /^[a-zA-Z0-9_]+$/;
29880     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29881     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29882
29883     // All these messages and functions are configurable
29884     return {
29885         /**
29886          * The function used to validate email addresses
29887          * @param {String} value The email address
29888          */
29889         'email' : function(v){
29890             return email.test(v);
29891         },
29892         /**
29893          * The error text to display when the email validation function returns false
29894          * @type String
29895          */
29896         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29897         /**
29898          * The keystroke filter mask to be applied on email input
29899          * @type RegExp
29900          */
29901         'emailMask' : /[a-z0-9_\.\-@]/i,
29902
29903         /**
29904          * The function used to validate URLs
29905          * @param {String} value The URL
29906          */
29907         'url' : function(v){
29908             return url.test(v);
29909         },
29910         /**
29911          * The error text to display when the url validation function returns false
29912          * @type String
29913          */
29914         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29915         
29916         /**
29917          * The function used to validate alpha values
29918          * @param {String} value The value
29919          */
29920         'alpha' : function(v){
29921             return alpha.test(v);
29922         },
29923         /**
29924          * The error text to display when the alpha validation function returns false
29925          * @type String
29926          */
29927         'alphaText' : 'This field should only contain letters and _',
29928         /**
29929          * The keystroke filter mask to be applied on alpha input
29930          * @type RegExp
29931          */
29932         'alphaMask' : /[a-z_]/i,
29933
29934         /**
29935          * The function used to validate alphanumeric values
29936          * @param {String} value The value
29937          */
29938         'alphanum' : function(v){
29939             return alphanum.test(v);
29940         },
29941         /**
29942          * The error text to display when the alphanumeric validation function returns false
29943          * @type String
29944          */
29945         'alphanumText' : 'This field should only contain letters, numbers and _',
29946         /**
29947          * The keystroke filter mask to be applied on alphanumeric input
29948          * @type RegExp
29949          */
29950         'alphanumMask' : /[a-z0-9_]/i
29951     };
29952 }();//<script type="text/javascript">
29953
29954 /**
29955  * @class Roo.form.FCKeditor
29956  * @extends Roo.form.TextArea
29957  * Wrapper around the FCKEditor http://www.fckeditor.net
29958  * @constructor
29959  * Creates a new FCKeditor
29960  * @param {Object} config Configuration options
29961  */
29962 Roo.form.FCKeditor = function(config){
29963     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29964     this.addEvents({
29965          /**
29966          * @event editorinit
29967          * Fired when the editor is initialized - you can add extra handlers here..
29968          * @param {FCKeditor} this
29969          * @param {Object} the FCK object.
29970          */
29971         editorinit : true
29972     });
29973     
29974     
29975 };
29976 Roo.form.FCKeditor.editors = { };
29977 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29978 {
29979     //defaultAutoCreate : {
29980     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29981     //},
29982     // private
29983     /**
29984      * @cfg {Object} fck options - see fck manual for details.
29985      */
29986     fckconfig : false,
29987     
29988     /**
29989      * @cfg {Object} fck toolbar set (Basic or Default)
29990      */
29991     toolbarSet : 'Basic',
29992     /**
29993      * @cfg {Object} fck BasePath
29994      */ 
29995     basePath : '/fckeditor/',
29996     
29997     
29998     frame : false,
29999     
30000     value : '',
30001     
30002    
30003     onRender : function(ct, position)
30004     {
30005         if(!this.el){
30006             this.defaultAutoCreate = {
30007                 tag: "textarea",
30008                 style:"width:300px;height:60px;",
30009                 autocomplete: "off"
30010             };
30011         }
30012         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30013         /*
30014         if(this.grow){
30015             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30016             if(this.preventScrollbars){
30017                 this.el.setStyle("overflow", "hidden");
30018             }
30019             this.el.setHeight(this.growMin);
30020         }
30021         */
30022         //console.log('onrender' + this.getId() );
30023         Roo.form.FCKeditor.editors[this.getId()] = this;
30024          
30025
30026         this.replaceTextarea() ;
30027         
30028     },
30029     
30030     getEditor : function() {
30031         return this.fckEditor;
30032     },
30033     /**
30034      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30035      * @param {Mixed} value The value to set
30036      */
30037     
30038     
30039     setValue : function(value)
30040     {
30041         //console.log('setValue: ' + value);
30042         
30043         if(typeof(value) == 'undefined') { // not sure why this is happending...
30044             return;
30045         }
30046         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30047         
30048         //if(!this.el || !this.getEditor()) {
30049         //    this.value = value;
30050             //this.setValue.defer(100,this,[value]);    
30051         //    return;
30052         //} 
30053         
30054         if(!this.getEditor()) {
30055             return;
30056         }
30057         
30058         this.getEditor().SetData(value);
30059         
30060         //
30061
30062     },
30063
30064     /**
30065      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30066      * @return {Mixed} value The field value
30067      */
30068     getValue : function()
30069     {
30070         
30071         if (this.frame && this.frame.dom.style.display == 'none') {
30072             return Roo.form.FCKeditor.superclass.getValue.call(this);
30073         }
30074         
30075         if(!this.el || !this.getEditor()) {
30076            
30077            // this.getValue.defer(100,this); 
30078             return this.value;
30079         }
30080        
30081         
30082         var value=this.getEditor().GetData();
30083         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30084         return Roo.form.FCKeditor.superclass.getValue.call(this);
30085         
30086
30087     },
30088
30089     /**
30090      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30091      * @return {Mixed} value The field value
30092      */
30093     getRawValue : function()
30094     {
30095         if (this.frame && this.frame.dom.style.display == 'none') {
30096             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30097         }
30098         
30099         if(!this.el || !this.getEditor()) {
30100             //this.getRawValue.defer(100,this); 
30101             return this.value;
30102             return;
30103         }
30104         
30105         
30106         
30107         var value=this.getEditor().GetData();
30108         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30109         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30110          
30111     },
30112     
30113     setSize : function(w,h) {
30114         
30115         
30116         
30117         //if (this.frame && this.frame.dom.style.display == 'none') {
30118         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30119         //    return;
30120         //}
30121         //if(!this.el || !this.getEditor()) {
30122         //    this.setSize.defer(100,this, [w,h]); 
30123         //    return;
30124         //}
30125         
30126         
30127         
30128         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30129         
30130         this.frame.dom.setAttribute('width', w);
30131         this.frame.dom.setAttribute('height', h);
30132         this.frame.setSize(w,h);
30133         
30134     },
30135     
30136     toggleSourceEdit : function(value) {
30137         
30138       
30139          
30140         this.el.dom.style.display = value ? '' : 'none';
30141         this.frame.dom.style.display = value ?  'none' : '';
30142         
30143     },
30144     
30145     
30146     focus: function(tag)
30147     {
30148         if (this.frame.dom.style.display == 'none') {
30149             return Roo.form.FCKeditor.superclass.focus.call(this);
30150         }
30151         if(!this.el || !this.getEditor()) {
30152             this.focus.defer(100,this, [tag]); 
30153             return;
30154         }
30155         
30156         
30157         
30158         
30159         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30160         this.getEditor().Focus();
30161         if (tgs.length) {
30162             if (!this.getEditor().Selection.GetSelection()) {
30163                 this.focus.defer(100,this, [tag]); 
30164                 return;
30165             }
30166             
30167             
30168             var r = this.getEditor().EditorDocument.createRange();
30169             r.setStart(tgs[0],0);
30170             r.setEnd(tgs[0],0);
30171             this.getEditor().Selection.GetSelection().removeAllRanges();
30172             this.getEditor().Selection.GetSelection().addRange(r);
30173             this.getEditor().Focus();
30174         }
30175         
30176     },
30177     
30178     
30179     
30180     replaceTextarea : function()
30181     {
30182         if ( document.getElementById( this.getId() + '___Frame' ) )
30183             return ;
30184         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30185         //{
30186             // We must check the elements firstly using the Id and then the name.
30187         var oTextarea = document.getElementById( this.getId() );
30188         
30189         var colElementsByName = document.getElementsByName( this.getId() ) ;
30190          
30191         oTextarea.style.display = 'none' ;
30192
30193         if ( oTextarea.tabIndex ) {            
30194             this.TabIndex = oTextarea.tabIndex ;
30195         }
30196         
30197         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30198         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30199         this.frame = Roo.get(this.getId() + '___Frame')
30200     },
30201     
30202     _getConfigHtml : function()
30203     {
30204         var sConfig = '' ;
30205
30206         for ( var o in this.fckconfig ) {
30207             sConfig += sConfig.length > 0  ? '&amp;' : '';
30208             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30209         }
30210
30211         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30212     },
30213     
30214     
30215     _getIFrameHtml : function()
30216     {
30217         var sFile = 'fckeditor.html' ;
30218         /* no idea what this is about..
30219         try
30220         {
30221             if ( (/fcksource=true/i).test( window.top.location.search ) )
30222                 sFile = 'fckeditor.original.html' ;
30223         }
30224         catch (e) { 
30225         */
30226
30227         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30228         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30229         
30230         
30231         var html = '<iframe id="' + this.getId() +
30232             '___Frame" src="' + sLink +
30233             '" width="' + this.width +
30234             '" height="' + this.height + '"' +
30235             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30236             ' frameborder="0" scrolling="no"></iframe>' ;
30237
30238         return html ;
30239     },
30240     
30241     _insertHtmlBefore : function( html, element )
30242     {
30243         if ( element.insertAdjacentHTML )       {
30244             // IE
30245             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30246         } else { // Gecko
30247             var oRange = document.createRange() ;
30248             oRange.setStartBefore( element ) ;
30249             var oFragment = oRange.createContextualFragment( html );
30250             element.parentNode.insertBefore( oFragment, element ) ;
30251         }
30252     }
30253     
30254     
30255   
30256     
30257     
30258     
30259     
30260
30261 });
30262
30263 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30264
30265 function FCKeditor_OnComplete(editorInstance){
30266     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30267     f.fckEditor = editorInstance;
30268     //console.log("loaded");
30269     f.fireEvent('editorinit', f, editorInstance);
30270
30271   
30272
30273  
30274
30275
30276
30277
30278
30279
30280
30281
30282
30283
30284
30285
30286
30287
30288
30289 //<script type="text/javascript">
30290 /**
30291  * @class Roo.form.GridField
30292  * @extends Roo.form.Field
30293  * Embed a grid (or editable grid into a form)
30294  * STATUS ALPHA
30295  * 
30296  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30297  * it needs 
30298  * xgrid.store = Roo.data.Store
30299  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30300  * xgrid.store.reader = Roo.data.JsonReader 
30301  * 
30302  * 
30303  * @constructor
30304  * Creates a new GridField
30305  * @param {Object} config Configuration options
30306  */
30307 Roo.form.GridField = function(config){
30308     Roo.form.GridField.superclass.constructor.call(this, config);
30309      
30310 };
30311
30312 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30313     /**
30314      * @cfg {Number} width  - used to restrict width of grid..
30315      */
30316     width : 100,
30317     /**
30318      * @cfg {Number} height - used to restrict height of grid..
30319      */
30320     height : 50,
30321      /**
30322      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30323          * 
30324          *}
30325      */
30326     xgrid : false, 
30327     /**
30328      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30329      * {tag: "input", type: "checkbox", autocomplete: "off"})
30330      */
30331    // defaultAutoCreate : { tag: 'div' },
30332     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30333     /**
30334      * @cfg {String} addTitle Text to include for adding a title.
30335      */
30336     addTitle : false,
30337     //
30338     onResize : function(){
30339         Roo.form.Field.superclass.onResize.apply(this, arguments);
30340     },
30341
30342     initEvents : function(){
30343         // Roo.form.Checkbox.superclass.initEvents.call(this);
30344         // has no events...
30345        
30346     },
30347
30348
30349     getResizeEl : function(){
30350         return this.wrap;
30351     },
30352
30353     getPositionEl : function(){
30354         return this.wrap;
30355     },
30356
30357     // private
30358     onRender : function(ct, position){
30359         
30360         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30361         var style = this.style;
30362         delete this.style;
30363         
30364         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30365         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30366         this.viewEl = this.wrap.createChild({ tag: 'div' });
30367         if (style) {
30368             this.viewEl.applyStyles(style);
30369         }
30370         if (this.width) {
30371             this.viewEl.setWidth(this.width);
30372         }
30373         if (this.height) {
30374             this.viewEl.setHeight(this.height);
30375         }
30376         //if(this.inputValue !== undefined){
30377         //this.setValue(this.value);
30378         
30379         
30380         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30381         
30382         
30383         this.grid.render();
30384         this.grid.getDataSource().on('remove', this.refreshValue, this);
30385         this.grid.getDataSource().on('update', this.refreshValue, this);
30386         this.grid.on('afteredit', this.refreshValue, this);
30387  
30388     },
30389      
30390     
30391     /**
30392      * Sets the value of the item. 
30393      * @param {String} either an object  or a string..
30394      */
30395     setValue : function(v){
30396         //this.value = v;
30397         v = v || []; // empty set..
30398         // this does not seem smart - it really only affects memoryproxy grids..
30399         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30400             var ds = this.grid.getDataSource();
30401             // assumes a json reader..
30402             var data = {}
30403             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30404             ds.loadData( data);
30405         }
30406         // clear selection so it does not get stale.
30407         if (this.grid.sm) { 
30408             this.grid.sm.clearSelections();
30409         }
30410         
30411         Roo.form.GridField.superclass.setValue.call(this, v);
30412         this.refreshValue();
30413         // should load data in the grid really....
30414     },
30415     
30416     // private
30417     refreshValue: function() {
30418          var val = [];
30419         this.grid.getDataSource().each(function(r) {
30420             val.push(r.data);
30421         });
30422         this.el.dom.value = Roo.encode(val);
30423     }
30424     
30425      
30426     
30427     
30428 });/*
30429  * Based on:
30430  * Ext JS Library 1.1.1
30431  * Copyright(c) 2006-2007, Ext JS, LLC.
30432  *
30433  * Originally Released Under LGPL - original licence link has changed is not relivant.
30434  *
30435  * Fork - LGPL
30436  * <script type="text/javascript">
30437  */
30438 /**
30439  * @class Roo.form.DisplayField
30440  * @extends Roo.form.Field
30441  * A generic Field to display non-editable data.
30442  * @constructor
30443  * Creates a new Display Field item.
30444  * @param {Object} config Configuration options
30445  */
30446 Roo.form.DisplayField = function(config){
30447     Roo.form.DisplayField.superclass.constructor.call(this, config);
30448     
30449 };
30450
30451 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30452     inputType:      'hidden',
30453     allowBlank:     true,
30454     readOnly:         true,
30455     
30456  
30457     /**
30458      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30459      */
30460     focusClass : undefined,
30461     /**
30462      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30463      */
30464     fieldClass: 'x-form-field',
30465     
30466      /**
30467      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30468      */
30469     valueRenderer: undefined,
30470     
30471     width: 100,
30472     /**
30473      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30474      * {tag: "input", type: "checkbox", autocomplete: "off"})
30475      */
30476      
30477  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30478
30479     onResize : function(){
30480         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30481         
30482     },
30483
30484     initEvents : function(){
30485         // Roo.form.Checkbox.superclass.initEvents.call(this);
30486         // has no events...
30487        
30488     },
30489
30490
30491     getResizeEl : function(){
30492         return this.wrap;
30493     },
30494
30495     getPositionEl : function(){
30496         return this.wrap;
30497     },
30498
30499     // private
30500     onRender : function(ct, position){
30501         
30502         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30503         //if(this.inputValue !== undefined){
30504         this.wrap = this.el.wrap();
30505         
30506         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30507         
30508         if (this.bodyStyle) {
30509             this.viewEl.applyStyles(this.bodyStyle);
30510         }
30511         //this.viewEl.setStyle('padding', '2px');
30512         
30513         this.setValue(this.value);
30514         
30515     },
30516 /*
30517     // private
30518     initValue : Roo.emptyFn,
30519
30520   */
30521
30522         // private
30523     onClick : function(){
30524         
30525     },
30526
30527     /**
30528      * Sets the checked state of the checkbox.
30529      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30530      */
30531     setValue : function(v){
30532         this.value = v;
30533         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30534         // this might be called before we have a dom element..
30535         if (!this.viewEl) {
30536             return;
30537         }
30538         this.viewEl.dom.innerHTML = html;
30539         Roo.form.DisplayField.superclass.setValue.call(this, v);
30540
30541     }
30542 });/*
30543  * 
30544  * Licence- LGPL
30545  * 
30546  */
30547
30548 /**
30549  * @class Roo.form.DayPicker
30550  * @extends Roo.form.Field
30551  * A Day picker show [M] [T] [W] ....
30552  * @constructor
30553  * Creates a new Day Picker
30554  * @param {Object} config Configuration options
30555  */
30556 Roo.form.DayPicker= function(config){
30557     Roo.form.DayPicker.superclass.constructor.call(this, config);
30558      
30559 };
30560
30561 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30562     /**
30563      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30564      */
30565     focusClass : undefined,
30566     /**
30567      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30568      */
30569     fieldClass: "x-form-field",
30570    
30571     /**
30572      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30573      * {tag: "input", type: "checkbox", autocomplete: "off"})
30574      */
30575     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30576     
30577    
30578     actionMode : 'viewEl', 
30579     //
30580     // private
30581  
30582     inputType : 'hidden',
30583     
30584      
30585     inputElement: false, // real input element?
30586     basedOn: false, // ????
30587     
30588     isFormField: true, // not sure where this is needed!!!!
30589
30590     onResize : function(){
30591         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30592         if(!this.boxLabel){
30593             this.el.alignTo(this.wrap, 'c-c');
30594         }
30595     },
30596
30597     initEvents : function(){
30598         Roo.form.Checkbox.superclass.initEvents.call(this);
30599         this.el.on("click", this.onClick,  this);
30600         this.el.on("change", this.onClick,  this);
30601     },
30602
30603
30604     getResizeEl : function(){
30605         return this.wrap;
30606     },
30607
30608     getPositionEl : function(){
30609         return this.wrap;
30610     },
30611
30612     
30613     // private
30614     onRender : function(ct, position){
30615         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30616        
30617         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30618         
30619         var r1 = '<table><tr>';
30620         var r2 = '<tr class="x-form-daypick-icons">';
30621         for (var i=0; i < 7; i++) {
30622             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30623             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30624         }
30625         
30626         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30627         viewEl.select('img').on('click', this.onClick, this);
30628         this.viewEl = viewEl;   
30629         
30630         
30631         // this will not work on Chrome!!!
30632         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30633         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30634         
30635         
30636           
30637
30638     },
30639
30640     // private
30641     initValue : Roo.emptyFn,
30642
30643     /**
30644      * Returns the checked state of the checkbox.
30645      * @return {Boolean} True if checked, else false
30646      */
30647     getValue : function(){
30648         return this.el.dom.value;
30649         
30650     },
30651
30652         // private
30653     onClick : function(e){ 
30654         //this.setChecked(!this.checked);
30655         Roo.get(e.target).toggleClass('x-menu-item-checked');
30656         this.refreshValue();
30657         //if(this.el.dom.checked != this.checked){
30658         //    this.setValue(this.el.dom.checked);
30659        // }
30660     },
30661     
30662     // private
30663     refreshValue : function()
30664     {
30665         var val = '';
30666         this.viewEl.select('img',true).each(function(e,i,n)  {
30667             val += e.is(".x-menu-item-checked") ? String(n) : '';
30668         });
30669         this.setValue(val, true);
30670     },
30671
30672     /**
30673      * Sets the checked state of the checkbox.
30674      * On is always based on a string comparison between inputValue and the param.
30675      * @param {Boolean/String} value - the value to set 
30676      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30677      */
30678     setValue : function(v,suppressEvent){
30679         if (!this.el.dom) {
30680             return;
30681         }
30682         var old = this.el.dom.value ;
30683         this.el.dom.value = v;
30684         if (suppressEvent) {
30685             return ;
30686         }
30687          
30688         // update display..
30689         this.viewEl.select('img',true).each(function(e,i,n)  {
30690             
30691             var on = e.is(".x-menu-item-checked");
30692             var newv = v.indexOf(String(n)) > -1;
30693             if (on != newv) {
30694                 e.toggleClass('x-menu-item-checked');
30695             }
30696             
30697         });
30698         
30699         
30700         this.fireEvent('change', this, v, old);
30701         
30702         
30703     },
30704    
30705     // handle setting of hidden value by some other method!!?!?
30706     setFromHidden: function()
30707     {
30708         if(!this.el){
30709             return;
30710         }
30711         //console.log("SET FROM HIDDEN");
30712         //alert('setFrom hidden');
30713         this.setValue(this.el.dom.value);
30714     },
30715     
30716     onDestroy : function()
30717     {
30718         if(this.viewEl){
30719             Roo.get(this.viewEl).remove();
30720         }
30721          
30722         Roo.form.DayPicker.superclass.onDestroy.call(this);
30723     }
30724
30725 });/*
30726  * RooJS Library 1.1.1
30727  * Copyright(c) 2008-2011  Alan Knowles
30728  *
30729  * License - LGPL
30730  */
30731  
30732
30733 /**
30734  * @class Roo.form.ComboCheck
30735  * @extends Roo.form.ComboBox
30736  * A combobox for multiple select items.
30737  *
30738  * FIXME - could do with a reset button..
30739  * 
30740  * @constructor
30741  * Create a new ComboCheck
30742  * @param {Object} config Configuration options
30743  */
30744 Roo.form.ComboCheck = function(config){
30745     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30746     // should verify some data...
30747     // like
30748     // hiddenName = required..
30749     // displayField = required
30750     // valudField == required
30751     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30752     var _t = this;
30753     Roo.each(req, function(e) {
30754         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30755             throw "Roo.form.ComboCheck : missing value for: " + e;
30756         }
30757     });
30758     
30759     
30760 };
30761
30762 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30763      
30764      
30765     editable : false,
30766      
30767     selectedClass: 'x-menu-item-checked', 
30768     
30769     // private
30770     onRender : function(ct, position){
30771         var _t = this;
30772         
30773         
30774         
30775         if(!this.tpl){
30776             var cls = 'x-combo-list';
30777
30778             
30779             this.tpl =  new Roo.Template({
30780                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30781                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30782                    '<span>{' + this.displayField + '}</span>' +
30783                     '</div>' 
30784                 
30785             });
30786         }
30787  
30788         
30789         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30790         this.view.singleSelect = false;
30791         this.view.multiSelect = true;
30792         this.view.toggleSelect = true;
30793         this.pageTb.add(new Roo.Toolbar.Fill(), {
30794             
30795             text: 'Done',
30796             handler: function()
30797             {
30798                 _t.collapse();
30799             }
30800         });
30801     },
30802     
30803     onViewOver : function(e, t){
30804         // do nothing...
30805         return;
30806         
30807     },
30808     
30809     onViewClick : function(doFocus,index){
30810         return;
30811         
30812     },
30813     select: function () {
30814         //Roo.log("SELECT CALLED");
30815     },
30816      
30817     selectByValue : function(xv, scrollIntoView){
30818         var ar = this.getValueArray();
30819         var sels = [];
30820         
30821         Roo.each(ar, function(v) {
30822             if(v === undefined || v === null){
30823                 return;
30824             }
30825             var r = this.findRecord(this.valueField, v);
30826             if(r){
30827                 sels.push(this.store.indexOf(r))
30828                 
30829             }
30830         },this);
30831         this.view.select(sels);
30832         return false;
30833     },
30834     
30835     
30836     
30837     onSelect : function(record, index){
30838        // Roo.log("onselect Called");
30839        // this is only called by the clear button now..
30840         this.view.clearSelections();
30841         this.setValue('[]');
30842         if (this.value != this.valueBefore) {
30843             this.fireEvent('change', this, this.value, this.valueBefore);
30844             this.valueBefore = this.value;
30845         }
30846     },
30847     getValueArray : function()
30848     {
30849         var ar = [] ;
30850         
30851         try {
30852             //Roo.log(this.value);
30853             if (typeof(this.value) == 'undefined') {
30854                 return [];
30855             }
30856             var ar = Roo.decode(this.value);
30857             return  ar instanceof Array ? ar : []; //?? valid?
30858             
30859         } catch(e) {
30860             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30861             return [];
30862         }
30863          
30864     },
30865     expand : function ()
30866     {
30867         
30868         Roo.form.ComboCheck.superclass.expand.call(this);
30869         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30870         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30871         
30872
30873     },
30874     
30875     collapse : function(){
30876         Roo.form.ComboCheck.superclass.collapse.call(this);
30877         var sl = this.view.getSelectedIndexes();
30878         var st = this.store;
30879         var nv = [];
30880         var tv = [];
30881         var r;
30882         Roo.each(sl, function(i) {
30883             r = st.getAt(i);
30884             nv.push(r.get(this.valueField));
30885         },this);
30886         this.setValue(Roo.encode(nv));
30887         if (this.value != this.valueBefore) {
30888
30889             this.fireEvent('change', this, this.value, this.valueBefore);
30890             this.valueBefore = this.value;
30891         }
30892         
30893     },
30894     
30895     setValue : function(v){
30896         // Roo.log(v);
30897         this.value = v;
30898         
30899         var vals = this.getValueArray();
30900         var tv = [];
30901         Roo.each(vals, function(k) {
30902             var r = this.findRecord(this.valueField, k);
30903             if(r){
30904                 tv.push(r.data[this.displayField]);
30905             }else if(this.valueNotFoundText !== undefined){
30906                 tv.push( this.valueNotFoundText );
30907             }
30908         },this);
30909        // Roo.log(tv);
30910         
30911         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30912         this.hiddenField.value = v;
30913         this.value = v;
30914     }
30915     
30916 });/*
30917  * Based on:
30918  * Ext JS Library 1.1.1
30919  * Copyright(c) 2006-2007, Ext JS, LLC.
30920  *
30921  * Originally Released Under LGPL - original licence link has changed is not relivant.
30922  *
30923  * Fork - LGPL
30924  * <script type="text/javascript">
30925  */
30926  
30927 /**
30928  * @class Roo.form.Signature
30929  * @extends Roo.form.Field
30930  * Signature field.  
30931  * @constructor
30932  * 
30933  * @param {Object} config Configuration options
30934  */
30935
30936 Roo.form.Signature = function(config){
30937     Roo.form.Signature.superclass.constructor.call(this, config);
30938     
30939     this.addEvents({// not in used??
30940          /**
30941          * @event confirm
30942          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30943              * @param {Roo.form.Signature} combo This combo box
30944              */
30945         'confirm' : true,
30946         /**
30947          * @event reset
30948          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30949              * @param {Roo.form.ComboBox} combo This combo box
30950              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30951              */
30952         'reset' : true
30953     });
30954 };
30955
30956 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30957     /**
30958      * @cfg {Object} labels Label to use when rendering a form.
30959      * defaults to 
30960      * labels : { 
30961      *      clear : "Clear",
30962      *      confirm : "Confirm"
30963      *  }
30964      */
30965     labels : { 
30966         clear : "Clear",
30967         confirm : "Confirm"
30968     },
30969     /**
30970      * @cfg {Number} width The signature panel width (defaults to 300)
30971      */
30972     width: 300,
30973     /**
30974      * @cfg {Number} height The signature panel height (defaults to 100)
30975      */
30976     height : 100,
30977     /**
30978      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30979      */
30980     allowBlank : false,
30981     
30982     //private
30983     // {Object} signPanel The signature SVG panel element (defaults to {})
30984     signPanel : {},
30985     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30986     isMouseDown : false,
30987     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30988     isConfirmed : false,
30989     // {String} signatureTmp SVG mapping string (defaults to empty string)
30990     signatureTmp : '',
30991     
30992     
30993     defaultAutoCreate : { // modified by initCompnoent..
30994         tag: "input",
30995         type:"hidden"
30996     },
30997
30998     // private
30999     onRender : function(ct, position){
31000         
31001         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31002         
31003         this.wrap = this.el.wrap({
31004             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31005         });
31006         
31007         this.createToolbar(this);
31008         this.signPanel = this.wrap.createChild({
31009                 tag: 'div',
31010                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31011             }, this.el
31012         );
31013             
31014         this.svgID = Roo.id();
31015         this.svgEl = this.signPanel.createChild({
31016               xmlns : 'http://www.w3.org/2000/svg',
31017               tag : 'svg',
31018               id : this.svgID + "-svg",
31019               width: this.width,
31020               height: this.height,
31021               viewBox: '0 0 '+this.width+' '+this.height,
31022               cn : [
31023                 {
31024                     tag: "rect",
31025                     id: this.svgID + "-svg-r",
31026                     width: this.width,
31027                     height: this.height,
31028                     fill: "#ffa"
31029                 },
31030                 {
31031                     tag: "line",
31032                     id: this.svgID + "-svg-l",
31033                     x1: "0", // start
31034                     y1: (this.height*0.8), // start set the line in 80% of height
31035                     x2: this.width, // end
31036                     y2: (this.height*0.8), // end set the line in 80% of height
31037                     'stroke': "#666",
31038                     'stroke-width': "1",
31039                     'stroke-dasharray': "3",
31040                     'shape-rendering': "crispEdges",
31041                     'pointer-events': "none"
31042                 },
31043                 {
31044                     tag: "path",
31045                     id: this.svgID + "-svg-p",
31046                     'stroke': "navy",
31047                     'stroke-width': "3",
31048                     'fill': "none",
31049                     'pointer-events': 'none'
31050                 }
31051               ]
31052         });
31053         this.createSVG();
31054         this.svgBox = this.svgEl.dom.getScreenCTM();
31055     },
31056     createSVG : function(){ 
31057         var svg = this.signPanel;
31058         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31059         var t = this;
31060
31061         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31062         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31063         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31064         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31065         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31066         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31067         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31068         
31069     },
31070     isTouchEvent : function(e){
31071         return e.type.match(/^touch/);
31072     },
31073     getCoords : function (e) {
31074         var pt    = this.svgEl.dom.createSVGPoint();
31075         pt.x = e.clientX; 
31076         pt.y = e.clientY;
31077         if (this.isTouchEvent(e)) {
31078             pt.x =  e.targetTouches[0].clientX 
31079             pt.y = e.targetTouches[0].clientY;
31080         }
31081         var a = this.svgEl.dom.getScreenCTM();
31082         var b = a.inverse();
31083         var mx = pt.matrixTransform(b);
31084         return mx.x + ',' + mx.y;
31085     },
31086     //mouse event headler 
31087     down : function (e) {
31088         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31089         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31090         
31091         this.isMouseDown = true;
31092         
31093         e.preventDefault();
31094     },
31095     move : function (e) {
31096         if (this.isMouseDown) {
31097             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31098             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31099         }
31100         
31101         e.preventDefault();
31102     },
31103     up : function (e) {
31104         this.isMouseDown = false;
31105         var sp = this.signatureTmp.split(' ');
31106         
31107         if(sp.length > 1){
31108             if(!sp[sp.length-2].match(/^L/)){
31109                 sp.pop();
31110                 sp.pop();
31111                 sp.push("");
31112                 this.signatureTmp = sp.join(" ");
31113             }
31114         }
31115         if(this.getValue() != this.signatureTmp){
31116             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31117             this.isConfirmed = false;
31118         }
31119         e.preventDefault();
31120     },
31121     
31122     /**
31123      * Protected method that will not generally be called directly. It
31124      * is called when the editor creates its toolbar. Override this method if you need to
31125      * add custom toolbar buttons.
31126      * @param {HtmlEditor} editor
31127      */
31128     createToolbar : function(editor){
31129          function btn(id, toggle, handler){
31130             var xid = fid + '-'+ id ;
31131             return {
31132                 id : xid,
31133                 cmd : id,
31134                 cls : 'x-btn-icon x-edit-'+id,
31135                 enableToggle:toggle !== false,
31136                 scope: editor, // was editor...
31137                 handler:handler||editor.relayBtnCmd,
31138                 clickEvent:'mousedown',
31139                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31140                 tabIndex:-1
31141             };
31142         }
31143         
31144         
31145         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31146         this.tb = tb;
31147         this.tb.add(
31148            {
31149                 cls : ' x-signature-btn x-signature-'+id,
31150                 scope: editor, // was editor...
31151                 handler: this.reset,
31152                 clickEvent:'mousedown',
31153                 text: this.labels.clear
31154             },
31155             {
31156                  xtype : 'Fill',
31157                  xns: Roo.Toolbar
31158             }, 
31159             {
31160                 cls : '  x-signature-btn x-signature-'+id,
31161                 scope: editor, // was editor...
31162                 handler: this.confirmHandler,
31163                 clickEvent:'mousedown',
31164                 text: this.labels.confirm
31165             }
31166         );
31167     
31168     },
31169     //public
31170     /**
31171      * when user is clicked confirm then show this image.....
31172      * 
31173      * @return {String} Image Data URI
31174      */
31175     getImageDataURI : function(){
31176         var svg = this.svgEl.dom.parentNode.innerHTML;
31177         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31178         return src; 
31179     },
31180     /**
31181      * 
31182      * @return {Boolean} this.isConfirmed
31183      */
31184     getConfirmed : function(){
31185         return this.isConfirmed;
31186     },
31187     /**
31188      * 
31189      * @return {Number} this.width
31190      */
31191     getWidth : function(){
31192         return this.width;
31193     },
31194     /**
31195      * 
31196      * @return {Number} this.height
31197      */
31198     getHeight : function(){
31199         return this.height;
31200     },
31201     // private
31202     getSignature : function(){
31203         return this.signatureTmp;
31204     },
31205     // private
31206     reset : function(){
31207         this.signatureTmp = '';
31208         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31209         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31210         this.isConfirmed = false;
31211         Roo.form.Signature.superclass.reset.call(this);
31212     },
31213     setSignature : function(s){
31214         this.signatureTmp = s;
31215         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31216         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31217         this.setValue(s);
31218         this.isConfirmed = false;
31219         Roo.form.Signature.superclass.reset.call(this);
31220     }, 
31221     test : function(){
31222 //        Roo.log(this.signPanel.dom.contentWindow.up())
31223     },
31224     //private
31225     setConfirmed : function(){
31226         
31227         
31228         
31229 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31230     },
31231     // private
31232     confirmHandler : function(){
31233         if(!this.getSignature()){
31234             return;
31235         }
31236         
31237         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31238         this.setValue(this.getSignature());
31239         this.isConfirmed = true;
31240         
31241         this.fireEvent('confirm', this);
31242     },
31243     // private
31244     // Subclasses should provide the validation implementation by overriding this
31245     validateValue : function(value){
31246         if(this.allowBlank){
31247             return true;
31248         }
31249         
31250         if(this.isConfirmed){
31251             return true;
31252         }
31253         return false;
31254     }
31255 });/*
31256  * Based on:
31257  * Ext JS Library 1.1.1
31258  * Copyright(c) 2006-2007, Ext JS, LLC.
31259  *
31260  * Originally Released Under LGPL - original licence link has changed is not relivant.
31261  *
31262  * Fork - LGPL
31263  * <script type="text/javascript">
31264  */
31265  
31266
31267 /**
31268  * @class Roo.form.ComboBox
31269  * @extends Roo.form.TriggerField
31270  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31271  * @constructor
31272  * Create a new ComboBox.
31273  * @param {Object} config Configuration options
31274  */
31275 Roo.form.Select = function(config){
31276     Roo.form.Select.superclass.constructor.call(this, config);
31277      
31278 };
31279
31280 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31281     /**
31282      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31283      */
31284     /**
31285      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31286      * rendering into an Roo.Editor, defaults to false)
31287      */
31288     /**
31289      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31290      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31291      */
31292     /**
31293      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31294      */
31295     /**
31296      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31297      * the dropdown list (defaults to undefined, with no header element)
31298      */
31299
31300      /**
31301      * @cfg {String/Roo.Template} tpl The template to use to render the output
31302      */
31303      
31304     // private
31305     defaultAutoCreate : {tag: "select"  },
31306     /**
31307      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31308      */
31309     listWidth: undefined,
31310     /**
31311      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31312      * mode = 'remote' or 'text' if mode = 'local')
31313      */
31314     displayField: undefined,
31315     /**
31316      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31317      * mode = 'remote' or 'value' if mode = 'local'). 
31318      * Note: use of a valueField requires the user make a selection
31319      * in order for a value to be mapped.
31320      */
31321     valueField: undefined,
31322     
31323     
31324     /**
31325      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31326      * field's data value (defaults to the underlying DOM element's name)
31327      */
31328     hiddenName: undefined,
31329     /**
31330      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31331      */
31332     listClass: '',
31333     /**
31334      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31335      */
31336     selectedClass: 'x-combo-selected',
31337     /**
31338      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31339      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31340      * which displays a downward arrow icon).
31341      */
31342     triggerClass : 'x-form-arrow-trigger',
31343     /**
31344      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31345      */
31346     shadow:'sides',
31347     /**
31348      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31349      * anchor positions (defaults to 'tl-bl')
31350      */
31351     listAlign: 'tl-bl?',
31352     /**
31353      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31354      */
31355     maxHeight: 300,
31356     /**
31357      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31358      * query specified by the allQuery config option (defaults to 'query')
31359      */
31360     triggerAction: 'query',
31361     /**
31362      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31363      * (defaults to 4, does not apply if editable = false)
31364      */
31365     minChars : 4,
31366     /**
31367      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31368      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31369      */
31370     typeAhead: false,
31371     /**
31372      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31373      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31374      */
31375     queryDelay: 500,
31376     /**
31377      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31378      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31379      */
31380     pageSize: 0,
31381     /**
31382      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31383      * when editable = true (defaults to false)
31384      */
31385     selectOnFocus:false,
31386     /**
31387      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31388      */
31389     queryParam: 'query',
31390     /**
31391      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31392      * when mode = 'remote' (defaults to 'Loading...')
31393      */
31394     loadingText: 'Loading...',
31395     /**
31396      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31397      */
31398     resizable: false,
31399     /**
31400      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31401      */
31402     handleHeight : 8,
31403     /**
31404      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31405      * traditional select (defaults to true)
31406      */
31407     editable: true,
31408     /**
31409      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31410      */
31411     allQuery: '',
31412     /**
31413      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31414      */
31415     mode: 'remote',
31416     /**
31417      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31418      * listWidth has a higher value)
31419      */
31420     minListWidth : 70,
31421     /**
31422      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31423      * allow the user to set arbitrary text into the field (defaults to false)
31424      */
31425     forceSelection:false,
31426     /**
31427      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31428      * if typeAhead = true (defaults to 250)
31429      */
31430     typeAheadDelay : 250,
31431     /**
31432      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31433      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31434      */
31435     valueNotFoundText : undefined,
31436     
31437     /**
31438      * @cfg {String} defaultValue The value displayed after loading the store.
31439      */
31440     defaultValue: '',
31441     
31442     /**
31443      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31444      */
31445     blockFocus : false,
31446     
31447     /**
31448      * @cfg {Boolean} disableClear Disable showing of clear button.
31449      */
31450     disableClear : false,
31451     /**
31452      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31453      */
31454     alwaysQuery : false,
31455     
31456     //private
31457     addicon : false,
31458     editicon: false,
31459     
31460     // element that contains real text value.. (when hidden is used..)
31461      
31462     // private
31463     onRender : function(ct, position){
31464         Roo.form.Field.prototype.onRender.call(this, ct, position);
31465         
31466         if(this.store){
31467             this.store.on('beforeload', this.onBeforeLoad, this);
31468             this.store.on('load', this.onLoad, this);
31469             this.store.on('loadexception', this.onLoadException, this);
31470             this.store.load({});
31471         }
31472         
31473         
31474         
31475     },
31476
31477     // private
31478     initEvents : function(){
31479         //Roo.form.ComboBox.superclass.initEvents.call(this);
31480  
31481     },
31482
31483     onDestroy : function(){
31484        
31485         if(this.store){
31486             this.store.un('beforeload', this.onBeforeLoad, this);
31487             this.store.un('load', this.onLoad, this);
31488             this.store.un('loadexception', this.onLoadException, this);
31489         }
31490         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31491     },
31492
31493     // private
31494     fireKey : function(e){
31495         if(e.isNavKeyPress() && !this.list.isVisible()){
31496             this.fireEvent("specialkey", this, e);
31497         }
31498     },
31499
31500     // private
31501     onResize: function(w, h){
31502         
31503         return; 
31504     
31505         
31506     },
31507
31508     /**
31509      * Allow or prevent the user from directly editing the field text.  If false is passed,
31510      * the user will only be able to select from the items defined in the dropdown list.  This method
31511      * is the runtime equivalent of setting the 'editable' config option at config time.
31512      * @param {Boolean} value True to allow the user to directly edit the field text
31513      */
31514     setEditable : function(value){
31515          
31516     },
31517
31518     // private
31519     onBeforeLoad : function(){
31520         
31521         Roo.log("Select before load");
31522         return;
31523     
31524         this.innerList.update(this.loadingText ?
31525                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31526         //this.restrictHeight();
31527         this.selectedIndex = -1;
31528     },
31529
31530     // private
31531     onLoad : function(){
31532
31533     
31534         var dom = this.el.dom;
31535         dom.innerHTML = '';
31536          var od = dom.ownerDocument;
31537          
31538         if (this.emptyText) {
31539             var op = od.createElement('option');
31540             op.setAttribute('value', '');
31541             op.innerHTML = String.format('{0}', this.emptyText);
31542             dom.appendChild(op);
31543         }
31544         if(this.store.getCount() > 0){
31545            
31546             var vf = this.valueField;
31547             var df = this.displayField;
31548             this.store.data.each(function(r) {
31549                 // which colmsn to use... testing - cdoe / title..
31550                 var op = od.createElement('option');
31551                 op.setAttribute('value', r.data[vf]);
31552                 op.innerHTML = String.format('{0}', r.data[df]);
31553                 dom.appendChild(op);
31554             });
31555             if (typeof(this.defaultValue != 'undefined')) {
31556                 this.setValue(this.defaultValue);
31557             }
31558             
31559              
31560         }else{
31561             //this.onEmptyResults();
31562         }
31563         //this.el.focus();
31564     },
31565     // private
31566     onLoadException : function()
31567     {
31568         dom.innerHTML = '';
31569             
31570         Roo.log("Select on load exception");
31571         return;
31572     
31573         this.collapse();
31574         Roo.log(this.store.reader.jsonData);
31575         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31576             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31577         }
31578         
31579         
31580     },
31581     // private
31582     onTypeAhead : function(){
31583          
31584     },
31585
31586     // private
31587     onSelect : function(record, index){
31588         Roo.log('on select?');
31589         return;
31590         if(this.fireEvent('beforeselect', this, record, index) !== false){
31591             this.setFromData(index > -1 ? record.data : false);
31592             this.collapse();
31593             this.fireEvent('select', this, record, index);
31594         }
31595     },
31596
31597     /**
31598      * Returns the currently selected field value or empty string if no value is set.
31599      * @return {String} value The selected value
31600      */
31601     getValue : function(){
31602         var dom = this.el.dom;
31603         this.value = dom.options[dom.selectedIndex].value;
31604         return this.value;
31605         
31606     },
31607
31608     /**
31609      * Clears any text/value currently set in the field
31610      */
31611     clearValue : function(){
31612         this.value = '';
31613         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31614         
31615     },
31616
31617     /**
31618      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31619      * will be displayed in the field.  If the value does not match the data value of an existing item,
31620      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31621      * Otherwise the field will be blank (although the value will still be set).
31622      * @param {String} value The value to match
31623      */
31624     setValue : function(v){
31625         var d = this.el.dom;
31626         for (var i =0; i < d.options.length;i++) {
31627             if (v == d.options[i].value) {
31628                 d.selectedIndex = i;
31629                 this.value = v;
31630                 return;
31631             }
31632         }
31633         this.clearValue();
31634     },
31635     /**
31636      * @property {Object} the last set data for the element
31637      */
31638     
31639     lastData : false,
31640     /**
31641      * Sets the value of the field based on a object which is related to the record format for the store.
31642      * @param {Object} value the value to set as. or false on reset?
31643      */
31644     setFromData : function(o){
31645         Roo.log('setfrom data?');
31646          
31647         
31648         
31649     },
31650     // private
31651     reset : function(){
31652         this.clearValue();
31653     },
31654     // private
31655     findRecord : function(prop, value){
31656         
31657         return false;
31658     
31659         var record;
31660         if(this.store.getCount() > 0){
31661             this.store.each(function(r){
31662                 if(r.data[prop] == value){
31663                     record = r;
31664                     return false;
31665                 }
31666                 return true;
31667             });
31668         }
31669         return record;
31670     },
31671     
31672     getName: function()
31673     {
31674         // returns hidden if it's set..
31675         if (!this.rendered) {return ''};
31676         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31677         
31678     },
31679      
31680
31681     
31682
31683     // private
31684     onEmptyResults : function(){
31685         Roo.log('empty results');
31686         //this.collapse();
31687     },
31688
31689     /**
31690      * Returns true if the dropdown list is expanded, else false.
31691      */
31692     isExpanded : function(){
31693         return false;
31694     },
31695
31696     /**
31697      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31698      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31699      * @param {String} value The data value of the item to select
31700      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31701      * selected item if it is not currently in view (defaults to true)
31702      * @return {Boolean} True if the value matched an item in the list, else false
31703      */
31704     selectByValue : function(v, scrollIntoView){
31705         Roo.log('select By Value');
31706         return false;
31707     
31708         if(v !== undefined && v !== null){
31709             var r = this.findRecord(this.valueField || this.displayField, v);
31710             if(r){
31711                 this.select(this.store.indexOf(r), scrollIntoView);
31712                 return true;
31713             }
31714         }
31715         return false;
31716     },
31717
31718     /**
31719      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31720      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31721      * @param {Number} index The zero-based index of the list item to select
31722      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31723      * selected item if it is not currently in view (defaults to true)
31724      */
31725     select : function(index, scrollIntoView){
31726         Roo.log('select ');
31727         return  ;
31728         
31729         this.selectedIndex = index;
31730         this.view.select(index);
31731         if(scrollIntoView !== false){
31732             var el = this.view.getNode(index);
31733             if(el){
31734                 this.innerList.scrollChildIntoView(el, false);
31735             }
31736         }
31737     },
31738
31739       
31740
31741     // private
31742     validateBlur : function(){
31743         
31744         return;
31745         
31746     },
31747
31748     // private
31749     initQuery : function(){
31750         this.doQuery(this.getRawValue());
31751     },
31752
31753     // private
31754     doForce : function(){
31755         if(this.el.dom.value.length > 0){
31756             this.el.dom.value =
31757                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31758              
31759         }
31760     },
31761
31762     /**
31763      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31764      * query allowing the query action to be canceled if needed.
31765      * @param {String} query The SQL query to execute
31766      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31767      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31768      * saved in the current store (defaults to false)
31769      */
31770     doQuery : function(q, forceAll){
31771         
31772         Roo.log('doQuery?');
31773         if(q === undefined || q === null){
31774             q = '';
31775         }
31776         var qe = {
31777             query: q,
31778             forceAll: forceAll,
31779             combo: this,
31780             cancel:false
31781         };
31782         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31783             return false;
31784         }
31785         q = qe.query;
31786         forceAll = qe.forceAll;
31787         if(forceAll === true || (q.length >= this.minChars)){
31788             if(this.lastQuery != q || this.alwaysQuery){
31789                 this.lastQuery = q;
31790                 if(this.mode == 'local'){
31791                     this.selectedIndex = -1;
31792                     if(forceAll){
31793                         this.store.clearFilter();
31794                     }else{
31795                         this.store.filter(this.displayField, q);
31796                     }
31797                     this.onLoad();
31798                 }else{
31799                     this.store.baseParams[this.queryParam] = q;
31800                     this.store.load({
31801                         params: this.getParams(q)
31802                     });
31803                     this.expand();
31804                 }
31805             }else{
31806                 this.selectedIndex = -1;
31807                 this.onLoad();   
31808             }
31809         }
31810     },
31811
31812     // private
31813     getParams : function(q){
31814         var p = {};
31815         //p[this.queryParam] = q;
31816         if(this.pageSize){
31817             p.start = 0;
31818             p.limit = this.pageSize;
31819         }
31820         return p;
31821     },
31822
31823     /**
31824      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31825      */
31826     collapse : function(){
31827         
31828     },
31829
31830     // private
31831     collapseIf : function(e){
31832         
31833     },
31834
31835     /**
31836      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31837      */
31838     expand : function(){
31839         
31840     } ,
31841
31842     // private
31843      
31844
31845     /** 
31846     * @cfg {Boolean} grow 
31847     * @hide 
31848     */
31849     /** 
31850     * @cfg {Number} growMin 
31851     * @hide 
31852     */
31853     /** 
31854     * @cfg {Number} growMax 
31855     * @hide 
31856     */
31857     /**
31858      * @hide
31859      * @method autoSize
31860      */
31861     
31862     setWidth : function()
31863     {
31864         
31865     },
31866     getResizeEl : function(){
31867         return this.el;
31868     }
31869 });//<script type="text/javasscript">
31870  
31871
31872 /**
31873  * @class Roo.DDView
31874  * A DnD enabled version of Roo.View.
31875  * @param {Element/String} container The Element in which to create the View.
31876  * @param {String} tpl The template string used to create the markup for each element of the View
31877  * @param {Object} config The configuration properties. These include all the config options of
31878  * {@link Roo.View} plus some specific to this class.<br>
31879  * <p>
31880  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31881  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31882  * <p>
31883  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31884 .x-view-drag-insert-above {
31885         border-top:1px dotted #3366cc;
31886 }
31887 .x-view-drag-insert-below {
31888         border-bottom:1px dotted #3366cc;
31889 }
31890 </code></pre>
31891  * 
31892  */
31893  
31894 Roo.DDView = function(container, tpl, config) {
31895     Roo.DDView.superclass.constructor.apply(this, arguments);
31896     this.getEl().setStyle("outline", "0px none");
31897     this.getEl().unselectable();
31898     if (this.dragGroup) {
31899                 this.setDraggable(this.dragGroup.split(","));
31900     }
31901     if (this.dropGroup) {
31902                 this.setDroppable(this.dropGroup.split(","));
31903     }
31904     if (this.deletable) {
31905         this.setDeletable();
31906     }
31907     this.isDirtyFlag = false;
31908         this.addEvents({
31909                 "drop" : true
31910         });
31911 };
31912
31913 Roo.extend(Roo.DDView, Roo.View, {
31914 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31915 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31916 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31917 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31918
31919         isFormField: true,
31920
31921         reset: Roo.emptyFn,
31922         
31923         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31924
31925         validate: function() {
31926                 return true;
31927         },
31928         
31929         destroy: function() {
31930                 this.purgeListeners();
31931                 this.getEl.removeAllListeners();
31932                 this.getEl().remove();
31933                 if (this.dragZone) {
31934                         if (this.dragZone.destroy) {
31935                                 this.dragZone.destroy();
31936                         }
31937                 }
31938                 if (this.dropZone) {
31939                         if (this.dropZone.destroy) {
31940                                 this.dropZone.destroy();
31941                         }
31942                 }
31943         },
31944
31945 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31946         getName: function() {
31947                 return this.name;
31948         },
31949
31950 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31951         setValue: function(v) {
31952                 if (!this.store) {
31953                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31954                 }
31955                 var data = {};
31956                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31957                 this.store.proxy = new Roo.data.MemoryProxy(data);
31958                 this.store.load();
31959         },
31960
31961 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31962         getValue: function() {
31963                 var result = '(';
31964                 this.store.each(function(rec) {
31965                         result += rec.id + ',';
31966                 });
31967                 return result.substr(0, result.length - 1) + ')';
31968         },
31969         
31970         getIds: function() {
31971                 var i = 0, result = new Array(this.store.getCount());
31972                 this.store.each(function(rec) {
31973                         result[i++] = rec.id;
31974                 });
31975                 return result;
31976         },
31977         
31978         isDirty: function() {
31979                 return this.isDirtyFlag;
31980         },
31981
31982 /**
31983  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31984  *      whole Element becomes the target, and this causes the drop gesture to append.
31985  */
31986     getTargetFromEvent : function(e) {
31987                 var target = e.getTarget();
31988                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31989                 target = target.parentNode;
31990                 }
31991                 if (!target) {
31992                         target = this.el.dom.lastChild || this.el.dom;
31993                 }
31994                 return target;
31995     },
31996
31997 /**
31998  *      Create the drag data which consists of an object which has the property "ddel" as
31999  *      the drag proxy element. 
32000  */
32001     getDragData : function(e) {
32002         var target = this.findItemFromChild(e.getTarget());
32003                 if(target) {
32004                         this.handleSelection(e);
32005                         var selNodes = this.getSelectedNodes();
32006             var dragData = {
32007                 source: this,
32008                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32009                 nodes: selNodes,
32010                 records: []
32011                         };
32012                         var selectedIndices = this.getSelectedIndexes();
32013                         for (var i = 0; i < selectedIndices.length; i++) {
32014                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32015                         }
32016                         if (selNodes.length == 1) {
32017                                 dragData.ddel = target.cloneNode(true); // the div element
32018                         } else {
32019                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32020                                 div.className = 'multi-proxy';
32021                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32022                                         div.appendChild(selNodes[i].cloneNode(true));
32023                                 }
32024                                 dragData.ddel = div;
32025                         }
32026             //console.log(dragData)
32027             //console.log(dragData.ddel.innerHTML)
32028                         return dragData;
32029                 }
32030         //console.log('nodragData')
32031                 return false;
32032     },
32033     
32034 /**     Specify to which ddGroup items in this DDView may be dragged. */
32035     setDraggable: function(ddGroup) {
32036         if (ddGroup instanceof Array) {
32037                 Roo.each(ddGroup, this.setDraggable, this);
32038                 return;
32039         }
32040         if (this.dragZone) {
32041                 this.dragZone.addToGroup(ddGroup);
32042         } else {
32043                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32044                                 containerScroll: true,
32045                                 ddGroup: ddGroup 
32046
32047                         });
32048 //                      Draggability implies selection. DragZone's mousedown selects the element.
32049                         if (!this.multiSelect) { this.singleSelect = true; }
32050
32051 //                      Wire the DragZone's handlers up to methods in *this*
32052                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32053                 }
32054     },
32055
32056 /**     Specify from which ddGroup this DDView accepts drops. */
32057     setDroppable: function(ddGroup) {
32058         if (ddGroup instanceof Array) {
32059                 Roo.each(ddGroup, this.setDroppable, this);
32060                 return;
32061         }
32062         if (this.dropZone) {
32063                 this.dropZone.addToGroup(ddGroup);
32064         } else {
32065                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32066                                 containerScroll: true,
32067                                 ddGroup: ddGroup
32068                         });
32069
32070 //                      Wire the DropZone's handlers up to methods in *this*
32071                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32072                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32073                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32074                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32075                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32076                 }
32077     },
32078
32079 /**     Decide whether to drop above or below a View node. */
32080     getDropPoint : function(e, n, dd){
32081         if (n == this.el.dom) { return "above"; }
32082                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32083                 var c = t + (b - t) / 2;
32084                 var y = Roo.lib.Event.getPageY(e);
32085                 if(y <= c) {
32086                         return "above";
32087                 }else{
32088                         return "below";
32089                 }
32090     },
32091
32092     onNodeEnter : function(n, dd, e, data){
32093                 return false;
32094     },
32095     
32096     onNodeOver : function(n, dd, e, data){
32097                 var pt = this.getDropPoint(e, n, dd);
32098                 // set the insert point style on the target node
32099                 var dragElClass = this.dropNotAllowed;
32100                 if (pt) {
32101                         var targetElClass;
32102                         if (pt == "above"){
32103                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32104                                 targetElClass = "x-view-drag-insert-above";
32105                         } else {
32106                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32107                                 targetElClass = "x-view-drag-insert-below";
32108                         }
32109                         if (this.lastInsertClass != targetElClass){
32110                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32111                                 this.lastInsertClass = targetElClass;
32112                         }
32113                 }
32114                 return dragElClass;
32115         },
32116
32117     onNodeOut : function(n, dd, e, data){
32118                 this.removeDropIndicators(n);
32119     },
32120
32121     onNodeDrop : function(n, dd, e, data){
32122         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32123                 return false;
32124         }
32125         var pt = this.getDropPoint(e, n, dd);
32126                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32127                 if (pt == "below") { insertAt++; }
32128                 for (var i = 0; i < data.records.length; i++) {
32129                         var r = data.records[i];
32130                         var dup = this.store.getById(r.id);
32131                         if (dup && (dd != this.dragZone)) {
32132                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32133                         } else {
32134                                 if (data.copy) {
32135                                         this.store.insert(insertAt++, r.copy());
32136                                 } else {
32137                                         data.source.isDirtyFlag = true;
32138                                         r.store.remove(r);
32139                                         this.store.insert(insertAt++, r);
32140                                 }
32141                                 this.isDirtyFlag = true;
32142                         }
32143                 }
32144                 this.dragZone.cachedTarget = null;
32145                 return true;
32146     },
32147
32148     removeDropIndicators : function(n){
32149                 if(n){
32150                         Roo.fly(n).removeClass([
32151                                 "x-view-drag-insert-above",
32152                                 "x-view-drag-insert-below"]);
32153                         this.lastInsertClass = "_noclass";
32154                 }
32155     },
32156
32157 /**
32158  *      Utility method. Add a delete option to the DDView's context menu.
32159  *      @param {String} imageUrl The URL of the "delete" icon image.
32160  */
32161         setDeletable: function(imageUrl) {
32162                 if (!this.singleSelect && !this.multiSelect) {
32163                         this.singleSelect = true;
32164                 }
32165                 var c = this.getContextMenu();
32166                 this.contextMenu.on("itemclick", function(item) {
32167                         switch (item.id) {
32168                                 case "delete":
32169                                         this.remove(this.getSelectedIndexes());
32170                                         break;
32171                         }
32172                 }, this);
32173                 this.contextMenu.add({
32174                         icon: imageUrl,
32175                         id: "delete",
32176                         text: 'Delete'
32177                 });
32178         },
32179         
32180 /**     Return the context menu for this DDView. */
32181         getContextMenu: function() {
32182                 if (!this.contextMenu) {
32183 //                      Create the View's context menu
32184                         this.contextMenu = new Roo.menu.Menu({
32185                                 id: this.id + "-contextmenu"
32186                         });
32187                         this.el.on("contextmenu", this.showContextMenu, this);
32188                 }
32189                 return this.contextMenu;
32190         },
32191         
32192         disableContextMenu: function() {
32193                 if (this.contextMenu) {
32194                         this.el.un("contextmenu", this.showContextMenu, this);
32195                 }
32196         },
32197
32198         showContextMenu: function(e, item) {
32199         item = this.findItemFromChild(e.getTarget());
32200                 if (item) {
32201                         e.stopEvent();
32202                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32203                         this.contextMenu.showAt(e.getXY());
32204             }
32205     },
32206
32207 /**
32208  *      Remove {@link Roo.data.Record}s at the specified indices.
32209  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32210  */
32211     remove: function(selectedIndices) {
32212                 selectedIndices = [].concat(selectedIndices);
32213                 for (var i = 0; i < selectedIndices.length; i++) {
32214                         var rec = this.store.getAt(selectedIndices[i]);
32215                         this.store.remove(rec);
32216                 }
32217     },
32218
32219 /**
32220  *      Double click fires the event, but also, if this is draggable, and there is only one other
32221  *      related DropZone, it transfers the selected node.
32222  */
32223     onDblClick : function(e){
32224         var item = this.findItemFromChild(e.getTarget());
32225         if(item){
32226             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32227                 return false;
32228             }
32229             if (this.dragGroup) {
32230                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32231                     while (targets.indexOf(this.dropZone) > -1) {
32232                             targets.remove(this.dropZone);
32233                                 }
32234                     if (targets.length == 1) {
32235                                         this.dragZone.cachedTarget = null;
32236                         var el = Roo.get(targets[0].getEl());
32237                         var box = el.getBox(true);
32238                         targets[0].onNodeDrop(el.dom, {
32239                                 target: el.dom,
32240                                 xy: [box.x, box.y + box.height - 1]
32241                         }, null, this.getDragData(e));
32242                     }
32243                 }
32244         }
32245     },
32246     
32247     handleSelection: function(e) {
32248                 this.dragZone.cachedTarget = null;
32249         var item = this.findItemFromChild(e.getTarget());
32250         if (!item) {
32251                 this.clearSelections(true);
32252                 return;
32253         }
32254                 if (item && (this.multiSelect || this.singleSelect)){
32255                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32256                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32257                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32258                                 this.unselect(item);
32259                         } else {
32260                                 this.select(item, this.multiSelect && e.ctrlKey);
32261                                 this.lastSelection = item;
32262                         }
32263                 }
32264     },
32265
32266     onItemClick : function(item, index, e){
32267                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32268                         return false;
32269                 }
32270                 return true;
32271     },
32272
32273     unselect : function(nodeInfo, suppressEvent){
32274                 var node = this.getNode(nodeInfo);
32275                 if(node && this.isSelected(node)){
32276                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32277                                 Roo.fly(node).removeClass(this.selectedClass);
32278                                 this.selections.remove(node);
32279                                 if(!suppressEvent){
32280                                         this.fireEvent("selectionchange", this, this.selections);
32281                                 }
32282                         }
32283                 }
32284     }
32285 });
32286 /*
32287  * Based on:
32288  * Ext JS Library 1.1.1
32289  * Copyright(c) 2006-2007, Ext JS, LLC.
32290  *
32291  * Originally Released Under LGPL - original licence link has changed is not relivant.
32292  *
32293  * Fork - LGPL
32294  * <script type="text/javascript">
32295  */
32296  
32297 /**
32298  * @class Roo.LayoutManager
32299  * @extends Roo.util.Observable
32300  * Base class for layout managers.
32301  */
32302 Roo.LayoutManager = function(container, config){
32303     Roo.LayoutManager.superclass.constructor.call(this);
32304     this.el = Roo.get(container);
32305     // ie scrollbar fix
32306     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32307         document.body.scroll = "no";
32308     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32309         this.el.position('relative');
32310     }
32311     this.id = this.el.id;
32312     this.el.addClass("x-layout-container");
32313     /** false to disable window resize monitoring @type Boolean */
32314     this.monitorWindowResize = true;
32315     this.regions = {};
32316     this.addEvents({
32317         /**
32318          * @event layout
32319          * Fires when a layout is performed. 
32320          * @param {Roo.LayoutManager} this
32321          */
32322         "layout" : true,
32323         /**
32324          * @event regionresized
32325          * Fires when the user resizes a region. 
32326          * @param {Roo.LayoutRegion} region The resized region
32327          * @param {Number} newSize The new size (width for east/west, height for north/south)
32328          */
32329         "regionresized" : true,
32330         /**
32331          * @event regioncollapsed
32332          * Fires when a region is collapsed. 
32333          * @param {Roo.LayoutRegion} region The collapsed region
32334          */
32335         "regioncollapsed" : true,
32336         /**
32337          * @event regionexpanded
32338          * Fires when a region is expanded.  
32339          * @param {Roo.LayoutRegion} region The expanded region
32340          */
32341         "regionexpanded" : true
32342     });
32343     this.updating = false;
32344     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32345 };
32346
32347 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32348     /**
32349      * Returns true if this layout is currently being updated
32350      * @return {Boolean}
32351      */
32352     isUpdating : function(){
32353         return this.updating; 
32354     },
32355     
32356     /**
32357      * Suspend the LayoutManager from doing auto-layouts while
32358      * making multiple add or remove calls
32359      */
32360     beginUpdate : function(){
32361         this.updating = true;    
32362     },
32363     
32364     /**
32365      * Restore auto-layouts and optionally disable the manager from performing a layout
32366      * @param {Boolean} noLayout true to disable a layout update 
32367      */
32368     endUpdate : function(noLayout){
32369         this.updating = false;
32370         if(!noLayout){
32371             this.layout();
32372         }    
32373     },
32374     
32375     layout: function(){
32376         
32377     },
32378     
32379     onRegionResized : function(region, newSize){
32380         this.fireEvent("regionresized", region, newSize);
32381         this.layout();
32382     },
32383     
32384     onRegionCollapsed : function(region){
32385         this.fireEvent("regioncollapsed", region);
32386     },
32387     
32388     onRegionExpanded : function(region){
32389         this.fireEvent("regionexpanded", region);
32390     },
32391         
32392     /**
32393      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32394      * performs box-model adjustments.
32395      * @return {Object} The size as an object {width: (the width), height: (the height)}
32396      */
32397     getViewSize : function(){
32398         var size;
32399         if(this.el.dom != document.body){
32400             size = this.el.getSize();
32401         }else{
32402             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32403         }
32404         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32405         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32406         return size;
32407     },
32408     
32409     /**
32410      * Returns the Element this layout is bound to.
32411      * @return {Roo.Element}
32412      */
32413     getEl : function(){
32414         return this.el;
32415     },
32416     
32417     /**
32418      * Returns the specified region.
32419      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32420      * @return {Roo.LayoutRegion}
32421      */
32422     getRegion : function(target){
32423         return this.regions[target.toLowerCase()];
32424     },
32425     
32426     onWindowResize : function(){
32427         if(this.monitorWindowResize){
32428             this.layout();
32429         }
32430     }
32431 });/*
32432  * Based on:
32433  * Ext JS Library 1.1.1
32434  * Copyright(c) 2006-2007, Ext JS, LLC.
32435  *
32436  * Originally Released Under LGPL - original licence link has changed is not relivant.
32437  *
32438  * Fork - LGPL
32439  * <script type="text/javascript">
32440  */
32441 /**
32442  * @class Roo.BorderLayout
32443  * @extends Roo.LayoutManager
32444  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32445  * please see: <br><br>
32446  * <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>
32447  * <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>
32448  * Example:
32449  <pre><code>
32450  var layout = new Roo.BorderLayout(document.body, {
32451     north: {
32452         initialSize: 25,
32453         titlebar: false
32454     },
32455     west: {
32456         split:true,
32457         initialSize: 200,
32458         minSize: 175,
32459         maxSize: 400,
32460         titlebar: true,
32461         collapsible: true
32462     },
32463     east: {
32464         split:true,
32465         initialSize: 202,
32466         minSize: 175,
32467         maxSize: 400,
32468         titlebar: true,
32469         collapsible: true
32470     },
32471     south: {
32472         split:true,
32473         initialSize: 100,
32474         minSize: 100,
32475         maxSize: 200,
32476         titlebar: true,
32477         collapsible: true
32478     },
32479     center: {
32480         titlebar: true,
32481         autoScroll:true,
32482         resizeTabs: true,
32483         minTabWidth: 50,
32484         preferredTabWidth: 150
32485     }
32486 });
32487
32488 // shorthand
32489 var CP = Roo.ContentPanel;
32490
32491 layout.beginUpdate();
32492 layout.add("north", new CP("north", "North"));
32493 layout.add("south", new CP("south", {title: "South", closable: true}));
32494 layout.add("west", new CP("west", {title: "West"}));
32495 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32496 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32497 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32498 layout.getRegion("center").showPanel("center1");
32499 layout.endUpdate();
32500 </code></pre>
32501
32502 <b>The container the layout is rendered into can be either the body element or any other element.
32503 If it is not the body element, the container needs to either be an absolute positioned element,
32504 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32505 the container size if it is not the body element.</b>
32506
32507 * @constructor
32508 * Create a new BorderLayout
32509 * @param {String/HTMLElement/Element} container The container this layout is bound to
32510 * @param {Object} config Configuration options
32511  */
32512 Roo.BorderLayout = function(container, config){
32513     config = config || {};
32514     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32515     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32516     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32517         var target = this.factory.validRegions[i];
32518         if(config[target]){
32519             this.addRegion(target, config[target]);
32520         }
32521     }
32522 };
32523
32524 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32525     /**
32526      * Creates and adds a new region if it doesn't already exist.
32527      * @param {String} target The target region key (north, south, east, west or center).
32528      * @param {Object} config The regions config object
32529      * @return {BorderLayoutRegion} The new region
32530      */
32531     addRegion : function(target, config){
32532         if(!this.regions[target]){
32533             var r = this.factory.create(target, this, config);
32534             this.bindRegion(target, r);
32535         }
32536         return this.regions[target];
32537     },
32538
32539     // private (kinda)
32540     bindRegion : function(name, r){
32541         this.regions[name] = r;
32542         r.on("visibilitychange", this.layout, this);
32543         r.on("paneladded", this.layout, this);
32544         r.on("panelremoved", this.layout, this);
32545         r.on("invalidated", this.layout, this);
32546         r.on("resized", this.onRegionResized, this);
32547         r.on("collapsed", this.onRegionCollapsed, this);
32548         r.on("expanded", this.onRegionExpanded, this);
32549     },
32550
32551     /**
32552      * Performs a layout update.
32553      */
32554     layout : function(){
32555         if(this.updating) return;
32556         var size = this.getViewSize();
32557         var w = size.width;
32558         var h = size.height;
32559         var centerW = w;
32560         var centerH = h;
32561         var centerY = 0;
32562         var centerX = 0;
32563         //var x = 0, y = 0;
32564
32565         var rs = this.regions;
32566         var north = rs["north"];
32567         var south = rs["south"]; 
32568         var west = rs["west"];
32569         var east = rs["east"];
32570         var center = rs["center"];
32571         //if(this.hideOnLayout){ // not supported anymore
32572             //c.el.setStyle("display", "none");
32573         //}
32574         if(north && north.isVisible()){
32575             var b = north.getBox();
32576             var m = north.getMargins();
32577             b.width = w - (m.left+m.right);
32578             b.x = m.left;
32579             b.y = m.top;
32580             centerY = b.height + b.y + m.bottom;
32581             centerH -= centerY;
32582             north.updateBox(this.safeBox(b));
32583         }
32584         if(south && south.isVisible()){
32585             var b = south.getBox();
32586             var m = south.getMargins();
32587             b.width = w - (m.left+m.right);
32588             b.x = m.left;
32589             var totalHeight = (b.height + m.top + m.bottom);
32590             b.y = h - totalHeight + m.top;
32591             centerH -= totalHeight;
32592             south.updateBox(this.safeBox(b));
32593         }
32594         if(west && west.isVisible()){
32595             var b = west.getBox();
32596             var m = west.getMargins();
32597             b.height = centerH - (m.top+m.bottom);
32598             b.x = m.left;
32599             b.y = centerY + m.top;
32600             var totalWidth = (b.width + m.left + m.right);
32601             centerX += totalWidth;
32602             centerW -= totalWidth;
32603             west.updateBox(this.safeBox(b));
32604         }
32605         if(east && east.isVisible()){
32606             var b = east.getBox();
32607             var m = east.getMargins();
32608             b.height = centerH - (m.top+m.bottom);
32609             var totalWidth = (b.width + m.left + m.right);
32610             b.x = w - totalWidth + m.left;
32611             b.y = centerY + m.top;
32612             centerW -= totalWidth;
32613             east.updateBox(this.safeBox(b));
32614         }
32615         if(center){
32616             var m = center.getMargins();
32617             var centerBox = {
32618                 x: centerX + m.left,
32619                 y: centerY + m.top,
32620                 width: centerW - (m.left+m.right),
32621                 height: centerH - (m.top+m.bottom)
32622             };
32623             //if(this.hideOnLayout){
32624                 //center.el.setStyle("display", "block");
32625             //}
32626             center.updateBox(this.safeBox(centerBox));
32627         }
32628         this.el.repaint();
32629         this.fireEvent("layout", this);
32630     },
32631
32632     // private
32633     safeBox : function(box){
32634         box.width = Math.max(0, box.width);
32635         box.height = Math.max(0, box.height);
32636         return box;
32637     },
32638
32639     /**
32640      * Adds a ContentPanel (or subclass) to this layout.
32641      * @param {String} target The target region key (north, south, east, west or center).
32642      * @param {Roo.ContentPanel} panel The panel to add
32643      * @return {Roo.ContentPanel} The added panel
32644      */
32645     add : function(target, panel){
32646          
32647         target = target.toLowerCase();
32648         return this.regions[target].add(panel);
32649     },
32650
32651     /**
32652      * Remove a ContentPanel (or subclass) to this layout.
32653      * @param {String} target The target region key (north, south, east, west or center).
32654      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32655      * @return {Roo.ContentPanel} The removed panel
32656      */
32657     remove : function(target, panel){
32658         target = target.toLowerCase();
32659         return this.regions[target].remove(panel);
32660     },
32661
32662     /**
32663      * Searches all regions for a panel with the specified id
32664      * @param {String} panelId
32665      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32666      */
32667     findPanel : function(panelId){
32668         var rs = this.regions;
32669         for(var target in rs){
32670             if(typeof rs[target] != "function"){
32671                 var p = rs[target].getPanel(panelId);
32672                 if(p){
32673                     return p;
32674                 }
32675             }
32676         }
32677         return null;
32678     },
32679
32680     /**
32681      * Searches all regions for a panel with the specified id and activates (shows) it.
32682      * @param {String/ContentPanel} panelId The panels id or the panel itself
32683      * @return {Roo.ContentPanel} The shown panel or null
32684      */
32685     showPanel : function(panelId) {
32686       var rs = this.regions;
32687       for(var target in rs){
32688          var r = rs[target];
32689          if(typeof r != "function"){
32690             if(r.hasPanel(panelId)){
32691                return r.showPanel(panelId);
32692             }
32693          }
32694       }
32695       return null;
32696    },
32697
32698    /**
32699      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32700      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32701      */
32702     restoreState : function(provider){
32703         if(!provider){
32704             provider = Roo.state.Manager;
32705         }
32706         var sm = new Roo.LayoutStateManager();
32707         sm.init(this, provider);
32708     },
32709
32710     /**
32711      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32712      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32713      * a valid ContentPanel config object.  Example:
32714      * <pre><code>
32715 // Create the main layout
32716 var layout = new Roo.BorderLayout('main-ct', {
32717     west: {
32718         split:true,
32719         minSize: 175,
32720         titlebar: true
32721     },
32722     center: {
32723         title:'Components'
32724     }
32725 }, 'main-ct');
32726
32727 // Create and add multiple ContentPanels at once via configs
32728 layout.batchAdd({
32729    west: {
32730        id: 'source-files',
32731        autoCreate:true,
32732        title:'Ext Source Files',
32733        autoScroll:true,
32734        fitToFrame:true
32735    },
32736    center : {
32737        el: cview,
32738        autoScroll:true,
32739        fitToFrame:true,
32740        toolbar: tb,
32741        resizeEl:'cbody'
32742    }
32743 });
32744 </code></pre>
32745      * @param {Object} regions An object containing ContentPanel configs by region name
32746      */
32747     batchAdd : function(regions){
32748         this.beginUpdate();
32749         for(var rname in regions){
32750             var lr = this.regions[rname];
32751             if(lr){
32752                 this.addTypedPanels(lr, regions[rname]);
32753             }
32754         }
32755         this.endUpdate();
32756     },
32757
32758     // private
32759     addTypedPanels : function(lr, ps){
32760         if(typeof ps == 'string'){
32761             lr.add(new Roo.ContentPanel(ps));
32762         }
32763         else if(ps instanceof Array){
32764             for(var i =0, len = ps.length; i < len; i++){
32765                 this.addTypedPanels(lr, ps[i]);
32766             }
32767         }
32768         else if(!ps.events){ // raw config?
32769             var el = ps.el;
32770             delete ps.el; // prevent conflict
32771             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32772         }
32773         else {  // panel object assumed!
32774             lr.add(ps);
32775         }
32776     },
32777     /**
32778      * Adds a xtype elements to the layout.
32779      * <pre><code>
32780
32781 layout.addxtype({
32782        xtype : 'ContentPanel',
32783        region: 'west',
32784        items: [ .... ]
32785    }
32786 );
32787
32788 layout.addxtype({
32789         xtype : 'NestedLayoutPanel',
32790         region: 'west',
32791         layout: {
32792            center: { },
32793            west: { }   
32794         },
32795         items : [ ... list of content panels or nested layout panels.. ]
32796    }
32797 );
32798 </code></pre>
32799      * @param {Object} cfg Xtype definition of item to add.
32800      */
32801     addxtype : function(cfg)
32802     {
32803         // basically accepts a pannel...
32804         // can accept a layout region..!?!?
32805         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32806         
32807         if (!cfg.xtype.match(/Panel$/)) {
32808             return false;
32809         }
32810         var ret = false;
32811         
32812         if (typeof(cfg.region) == 'undefined') {
32813             Roo.log("Failed to add Panel, region was not set");
32814             Roo.log(cfg);
32815             return false;
32816         }
32817         var region = cfg.region;
32818         delete cfg.region;
32819         
32820           
32821         var xitems = [];
32822         if (cfg.items) {
32823             xitems = cfg.items;
32824             delete cfg.items;
32825         }
32826         var nb = false;
32827         
32828         switch(cfg.xtype) 
32829         {
32830             case 'ContentPanel':  // ContentPanel (el, cfg)
32831             case 'ScrollPanel':  // ContentPanel (el, cfg)
32832             case 'ViewPanel': 
32833                 if(cfg.autoCreate) {
32834                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32835                 } else {
32836                     var el = this.el.createChild();
32837                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32838                 }
32839                 
32840                 this.add(region, ret);
32841                 break;
32842             
32843             
32844             case 'TreePanel': // our new panel!
32845                 cfg.el = this.el.createChild();
32846                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32847                 this.add(region, ret);
32848                 break;
32849             
32850             case 'NestedLayoutPanel': 
32851                 // create a new Layout (which is  a Border Layout...
32852                 var el = this.el.createChild();
32853                 var clayout = cfg.layout;
32854                 delete cfg.layout;
32855                 clayout.items   = clayout.items  || [];
32856                 // replace this exitems with the clayout ones..
32857                 xitems = clayout.items;
32858                  
32859                 
32860                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32861                     cfg.background = false;
32862                 }
32863                 var layout = new Roo.BorderLayout(el, clayout);
32864                 
32865                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32866                 //console.log('adding nested layout panel '  + cfg.toSource());
32867                 this.add(region, ret);
32868                 nb = {}; /// find first...
32869                 break;
32870                 
32871             case 'GridPanel': 
32872             
32873                 // needs grid and region
32874                 
32875                 //var el = this.getRegion(region).el.createChild();
32876                 var el = this.el.createChild();
32877                 // create the grid first...
32878                 
32879                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32880                 delete cfg.grid;
32881                 if (region == 'center' && this.active ) {
32882                     cfg.background = false;
32883                 }
32884                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32885                 
32886                 this.add(region, ret);
32887                 if (cfg.background) {
32888                     ret.on('activate', function(gp) {
32889                         if (!gp.grid.rendered) {
32890                             gp.grid.render();
32891                         }
32892                     });
32893                 } else {
32894                     grid.render();
32895                 }
32896                 break;
32897            
32898            
32899            
32900                 
32901                 
32902                 
32903             default:
32904                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32905                     
32906                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32907                     this.add(region, ret);
32908                 } else {
32909                 
32910                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32911                     return null;
32912                 }
32913                 
32914              // GridPanel (grid, cfg)
32915             
32916         }
32917         this.beginUpdate();
32918         // add children..
32919         var region = '';
32920         var abn = {};
32921         Roo.each(xitems, function(i)  {
32922             region = nb && i.region ? i.region : false;
32923             
32924             var add = ret.addxtype(i);
32925            
32926             if (region) {
32927                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32928                 if (!i.background) {
32929                     abn[region] = nb[region] ;
32930                 }
32931             }
32932             
32933         });
32934         this.endUpdate();
32935
32936         // make the last non-background panel active..
32937         //if (nb) { Roo.log(abn); }
32938         if (nb) {
32939             
32940             for(var r in abn) {
32941                 region = this.getRegion(r);
32942                 if (region) {
32943                     // tried using nb[r], but it does not work..
32944                      
32945                     region.showPanel(abn[r]);
32946                    
32947                 }
32948             }
32949         }
32950         return ret;
32951         
32952     }
32953 });
32954
32955 /**
32956  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32957  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32958  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32959  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32960  * <pre><code>
32961 // shorthand
32962 var CP = Roo.ContentPanel;
32963
32964 var layout = Roo.BorderLayout.create({
32965     north: {
32966         initialSize: 25,
32967         titlebar: false,
32968         panels: [new CP("north", "North")]
32969     },
32970     west: {
32971         split:true,
32972         initialSize: 200,
32973         minSize: 175,
32974         maxSize: 400,
32975         titlebar: true,
32976         collapsible: true,
32977         panels: [new CP("west", {title: "West"})]
32978     },
32979     east: {
32980         split:true,
32981         initialSize: 202,
32982         minSize: 175,
32983         maxSize: 400,
32984         titlebar: true,
32985         collapsible: true,
32986         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32987     },
32988     south: {
32989         split:true,
32990         initialSize: 100,
32991         minSize: 100,
32992         maxSize: 200,
32993         titlebar: true,
32994         collapsible: true,
32995         panels: [new CP("south", {title: "South", closable: true})]
32996     },
32997     center: {
32998         titlebar: true,
32999         autoScroll:true,
33000         resizeTabs: true,
33001         minTabWidth: 50,
33002         preferredTabWidth: 150,
33003         panels: [
33004             new CP("center1", {title: "Close Me", closable: true}),
33005             new CP("center2", {title: "Center Panel", closable: false})
33006         ]
33007     }
33008 }, document.body);
33009
33010 layout.getRegion("center").showPanel("center1");
33011 </code></pre>
33012  * @param config
33013  * @param targetEl
33014  */
33015 Roo.BorderLayout.create = function(config, targetEl){
33016     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33017     layout.beginUpdate();
33018     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33019     for(var j = 0, jlen = regions.length; j < jlen; j++){
33020         var lr = regions[j];
33021         if(layout.regions[lr] && config[lr].panels){
33022             var r = layout.regions[lr];
33023             var ps = config[lr].panels;
33024             layout.addTypedPanels(r, ps);
33025         }
33026     }
33027     layout.endUpdate();
33028     return layout;
33029 };
33030
33031 // private
33032 Roo.BorderLayout.RegionFactory = {
33033     // private
33034     validRegions : ["north","south","east","west","center"],
33035
33036     // private
33037     create : function(target, mgr, config){
33038         target = target.toLowerCase();
33039         if(config.lightweight || config.basic){
33040             return new Roo.BasicLayoutRegion(mgr, config, target);
33041         }
33042         switch(target){
33043             case "north":
33044                 return new Roo.NorthLayoutRegion(mgr, config);
33045             case "south":
33046                 return new Roo.SouthLayoutRegion(mgr, config);
33047             case "east":
33048                 return new Roo.EastLayoutRegion(mgr, config);
33049             case "west":
33050                 return new Roo.WestLayoutRegion(mgr, config);
33051             case "center":
33052                 return new Roo.CenterLayoutRegion(mgr, config);
33053         }
33054         throw 'Layout region "'+target+'" not supported.';
33055     }
33056 };/*
33057  * Based on:
33058  * Ext JS Library 1.1.1
33059  * Copyright(c) 2006-2007, Ext JS, LLC.
33060  *
33061  * Originally Released Under LGPL - original licence link has changed is not relivant.
33062  *
33063  * Fork - LGPL
33064  * <script type="text/javascript">
33065  */
33066  
33067 /**
33068  * @class Roo.BasicLayoutRegion
33069  * @extends Roo.util.Observable
33070  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33071  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33072  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33073  */
33074 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33075     this.mgr = mgr;
33076     this.position  = pos;
33077     this.events = {
33078         /**
33079          * @scope Roo.BasicLayoutRegion
33080          */
33081         
33082         /**
33083          * @event beforeremove
33084          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33085          * @param {Roo.LayoutRegion} this
33086          * @param {Roo.ContentPanel} panel The panel
33087          * @param {Object} e The cancel event object
33088          */
33089         "beforeremove" : true,
33090         /**
33091          * @event invalidated
33092          * Fires when the layout for this region is changed.
33093          * @param {Roo.LayoutRegion} this
33094          */
33095         "invalidated" : true,
33096         /**
33097          * @event visibilitychange
33098          * Fires when this region is shown or hidden 
33099          * @param {Roo.LayoutRegion} this
33100          * @param {Boolean} visibility true or false
33101          */
33102         "visibilitychange" : true,
33103         /**
33104          * @event paneladded
33105          * Fires when a panel is added. 
33106          * @param {Roo.LayoutRegion} this
33107          * @param {Roo.ContentPanel} panel The panel
33108          */
33109         "paneladded" : true,
33110         /**
33111          * @event panelremoved
33112          * Fires when a panel is removed. 
33113          * @param {Roo.LayoutRegion} this
33114          * @param {Roo.ContentPanel} panel The panel
33115          */
33116         "panelremoved" : true,
33117         /**
33118          * @event collapsed
33119          * Fires when this region is collapsed.
33120          * @param {Roo.LayoutRegion} this
33121          */
33122         "collapsed" : true,
33123         /**
33124          * @event expanded
33125          * Fires when this region is expanded.
33126          * @param {Roo.LayoutRegion} this
33127          */
33128         "expanded" : true,
33129         /**
33130          * @event slideshow
33131          * Fires when this region is slid into view.
33132          * @param {Roo.LayoutRegion} this
33133          */
33134         "slideshow" : true,
33135         /**
33136          * @event slidehide
33137          * Fires when this region slides out of view. 
33138          * @param {Roo.LayoutRegion} this
33139          */
33140         "slidehide" : true,
33141         /**
33142          * @event panelactivated
33143          * Fires when a panel is activated. 
33144          * @param {Roo.LayoutRegion} this
33145          * @param {Roo.ContentPanel} panel The activated panel
33146          */
33147         "panelactivated" : true,
33148         /**
33149          * @event resized
33150          * Fires when the user resizes this region. 
33151          * @param {Roo.LayoutRegion} this
33152          * @param {Number} newSize The new size (width for east/west, height for north/south)
33153          */
33154         "resized" : true
33155     };
33156     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33157     this.panels = new Roo.util.MixedCollection();
33158     this.panels.getKey = this.getPanelId.createDelegate(this);
33159     this.box = null;
33160     this.activePanel = null;
33161     // ensure listeners are added...
33162     
33163     if (config.listeners || config.events) {
33164         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33165             listeners : config.listeners || {},
33166             events : config.events || {}
33167         });
33168     }
33169     
33170     if(skipConfig !== true){
33171         this.applyConfig(config);
33172     }
33173 };
33174
33175 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33176     getPanelId : function(p){
33177         return p.getId();
33178     },
33179     
33180     applyConfig : function(config){
33181         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33182         this.config = config;
33183         
33184     },
33185     
33186     /**
33187      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33188      * the width, for horizontal (north, south) the height.
33189      * @param {Number} newSize The new width or height
33190      */
33191     resizeTo : function(newSize){
33192         var el = this.el ? this.el :
33193                  (this.activePanel ? this.activePanel.getEl() : null);
33194         if(el){
33195             switch(this.position){
33196                 case "east":
33197                 case "west":
33198                     el.setWidth(newSize);
33199                     this.fireEvent("resized", this, newSize);
33200                 break;
33201                 case "north":
33202                 case "south":
33203                     el.setHeight(newSize);
33204                     this.fireEvent("resized", this, newSize);
33205                 break;                
33206             }
33207         }
33208     },
33209     
33210     getBox : function(){
33211         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33212     },
33213     
33214     getMargins : function(){
33215         return this.margins;
33216     },
33217     
33218     updateBox : function(box){
33219         this.box = box;
33220         var el = this.activePanel.getEl();
33221         el.dom.style.left = box.x + "px";
33222         el.dom.style.top = box.y + "px";
33223         this.activePanel.setSize(box.width, box.height);
33224     },
33225     
33226     /**
33227      * Returns the container element for this region.
33228      * @return {Roo.Element}
33229      */
33230     getEl : function(){
33231         return this.activePanel;
33232     },
33233     
33234     /**
33235      * Returns true if this region is currently visible.
33236      * @return {Boolean}
33237      */
33238     isVisible : function(){
33239         return this.activePanel ? true : false;
33240     },
33241     
33242     setActivePanel : function(panel){
33243         panel = this.getPanel(panel);
33244         if(this.activePanel && this.activePanel != panel){
33245             this.activePanel.setActiveState(false);
33246             this.activePanel.getEl().setLeftTop(-10000,-10000);
33247         }
33248         this.activePanel = panel;
33249         panel.setActiveState(true);
33250         if(this.box){
33251             panel.setSize(this.box.width, this.box.height);
33252         }
33253         this.fireEvent("panelactivated", this, panel);
33254         this.fireEvent("invalidated");
33255     },
33256     
33257     /**
33258      * Show the specified panel.
33259      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33260      * @return {Roo.ContentPanel} The shown panel or null
33261      */
33262     showPanel : function(panel){
33263         if(panel = this.getPanel(panel)){
33264             this.setActivePanel(panel);
33265         }
33266         return panel;
33267     },
33268     
33269     /**
33270      * Get the active panel for this region.
33271      * @return {Roo.ContentPanel} The active panel or null
33272      */
33273     getActivePanel : function(){
33274         return this.activePanel;
33275     },
33276     
33277     /**
33278      * Add the passed ContentPanel(s)
33279      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33280      * @return {Roo.ContentPanel} The panel added (if only one was added)
33281      */
33282     add : function(panel){
33283         if(arguments.length > 1){
33284             for(var i = 0, len = arguments.length; i < len; i++) {
33285                 this.add(arguments[i]);
33286             }
33287             return null;
33288         }
33289         if(this.hasPanel(panel)){
33290             this.showPanel(panel);
33291             return panel;
33292         }
33293         var el = panel.getEl();
33294         if(el.dom.parentNode != this.mgr.el.dom){
33295             this.mgr.el.dom.appendChild(el.dom);
33296         }
33297         if(panel.setRegion){
33298             panel.setRegion(this);
33299         }
33300         this.panels.add(panel);
33301         el.setStyle("position", "absolute");
33302         if(!panel.background){
33303             this.setActivePanel(panel);
33304             if(this.config.initialSize && this.panels.getCount()==1){
33305                 this.resizeTo(this.config.initialSize);
33306             }
33307         }
33308         this.fireEvent("paneladded", this, panel);
33309         return panel;
33310     },
33311     
33312     /**
33313      * Returns true if the panel is in this region.
33314      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33315      * @return {Boolean}
33316      */
33317     hasPanel : function(panel){
33318         if(typeof panel == "object"){ // must be panel obj
33319             panel = panel.getId();
33320         }
33321         return this.getPanel(panel) ? true : false;
33322     },
33323     
33324     /**
33325      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33326      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33327      * @param {Boolean} preservePanel Overrides the config preservePanel option
33328      * @return {Roo.ContentPanel} The panel that was removed
33329      */
33330     remove : function(panel, preservePanel){
33331         panel = this.getPanel(panel);
33332         if(!panel){
33333             return null;
33334         }
33335         var e = {};
33336         this.fireEvent("beforeremove", this, panel, e);
33337         if(e.cancel === true){
33338             return null;
33339         }
33340         var panelId = panel.getId();
33341         this.panels.removeKey(panelId);
33342         return panel;
33343     },
33344     
33345     /**
33346      * Returns the panel specified or null if it's not in this region.
33347      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33348      * @return {Roo.ContentPanel}
33349      */
33350     getPanel : function(id){
33351         if(typeof id == "object"){ // must be panel obj
33352             return id;
33353         }
33354         return this.panels.get(id);
33355     },
33356     
33357     /**
33358      * Returns this regions position (north/south/east/west/center).
33359      * @return {String} 
33360      */
33361     getPosition: function(){
33362         return this.position;    
33363     }
33364 });/*
33365  * Based on:
33366  * Ext JS Library 1.1.1
33367  * Copyright(c) 2006-2007, Ext JS, LLC.
33368  *
33369  * Originally Released Under LGPL - original licence link has changed is not relivant.
33370  *
33371  * Fork - LGPL
33372  * <script type="text/javascript">
33373  */
33374  
33375 /**
33376  * @class Roo.LayoutRegion
33377  * @extends Roo.BasicLayoutRegion
33378  * This class represents a region in a layout manager.
33379  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33380  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33381  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33382  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33383  * @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})
33384  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33385  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33386  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33387  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33388  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33389  * @cfg {String}    title           The title for the region (overrides panel titles)
33390  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33391  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33392  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33393  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33394  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33395  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33396  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33397  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33398  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33399  * @cfg {Boolean}   showPin         True to show a pin button
33400  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33401  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33402  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33403  * @cfg {Number}    width           For East/West panels
33404  * @cfg {Number}    height          For North/South panels
33405  * @cfg {Boolean}   split           To show the splitter
33406  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33407  */
33408 Roo.LayoutRegion = function(mgr, config, pos){
33409     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33410     var dh = Roo.DomHelper;
33411     /** This region's container element 
33412     * @type Roo.Element */
33413     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33414     /** This region's title element 
33415     * @type Roo.Element */
33416
33417     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33418         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33419         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33420     ]}, true);
33421     this.titleEl.enableDisplayMode();
33422     /** This region's title text element 
33423     * @type HTMLElement */
33424     this.titleTextEl = this.titleEl.dom.firstChild;
33425     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33426     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33427     this.closeBtn.enableDisplayMode();
33428     this.closeBtn.on("click", this.closeClicked, this);
33429     this.closeBtn.hide();
33430
33431     this.createBody(config);
33432     this.visible = true;
33433     this.collapsed = false;
33434
33435     if(config.hideWhenEmpty){
33436         this.hide();
33437         this.on("paneladded", this.validateVisibility, this);
33438         this.on("panelremoved", this.validateVisibility, this);
33439     }
33440     this.applyConfig(config);
33441 };
33442
33443 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33444
33445     createBody : function(){
33446         /** This region's body element 
33447         * @type Roo.Element */
33448         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33449     },
33450
33451     applyConfig : function(c){
33452         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33453             var dh = Roo.DomHelper;
33454             if(c.titlebar !== false){
33455                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33456                 this.collapseBtn.on("click", this.collapse, this);
33457                 this.collapseBtn.enableDisplayMode();
33458
33459                 if(c.showPin === true || this.showPin){
33460                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33461                     this.stickBtn.enableDisplayMode();
33462                     this.stickBtn.on("click", this.expand, this);
33463                     this.stickBtn.hide();
33464                 }
33465             }
33466             /** This region's collapsed element
33467             * @type Roo.Element */
33468             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33469                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33470             ]}, true);
33471             if(c.floatable !== false){
33472                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33473                this.collapsedEl.on("click", this.collapseClick, this);
33474             }
33475
33476             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33477                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33478                    id: "message", unselectable: "on", style:{"float":"left"}});
33479                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33480              }
33481             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33482             this.expandBtn.on("click", this.expand, this);
33483         }
33484         if(this.collapseBtn){
33485             this.collapseBtn.setVisible(c.collapsible == true);
33486         }
33487         this.cmargins = c.cmargins || this.cmargins ||
33488                          (this.position == "west" || this.position == "east" ?
33489                              {top: 0, left: 2, right:2, bottom: 0} :
33490                              {top: 2, left: 0, right:0, bottom: 2});
33491         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33492         this.bottomTabs = c.tabPosition != "top";
33493         this.autoScroll = c.autoScroll || false;
33494         if(this.autoScroll){
33495             this.bodyEl.setStyle("overflow", "auto");
33496         }else{
33497             this.bodyEl.setStyle("overflow", "hidden");
33498         }
33499         //if(c.titlebar !== false){
33500             if((!c.titlebar && !c.title) || c.titlebar === false){
33501                 this.titleEl.hide();
33502             }else{
33503                 this.titleEl.show();
33504                 if(c.title){
33505                     this.titleTextEl.innerHTML = c.title;
33506                 }
33507             }
33508         //}
33509         this.duration = c.duration || .30;
33510         this.slideDuration = c.slideDuration || .45;
33511         this.config = c;
33512         if(c.collapsed){
33513             this.collapse(true);
33514         }
33515         if(c.hidden){
33516             this.hide();
33517         }
33518     },
33519     /**
33520      * Returns true if this region is currently visible.
33521      * @return {Boolean}
33522      */
33523     isVisible : function(){
33524         return this.visible;
33525     },
33526
33527     /**
33528      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33529      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33530      */
33531     setCollapsedTitle : function(title){
33532         title = title || "&#160;";
33533         if(this.collapsedTitleTextEl){
33534             this.collapsedTitleTextEl.innerHTML = title;
33535         }
33536     },
33537
33538     getBox : function(){
33539         var b;
33540         if(!this.collapsed){
33541             b = this.el.getBox(false, true);
33542         }else{
33543             b = this.collapsedEl.getBox(false, true);
33544         }
33545         return b;
33546     },
33547
33548     getMargins : function(){
33549         return this.collapsed ? this.cmargins : this.margins;
33550     },
33551
33552     highlight : function(){
33553         this.el.addClass("x-layout-panel-dragover");
33554     },
33555
33556     unhighlight : function(){
33557         this.el.removeClass("x-layout-panel-dragover");
33558     },
33559
33560     updateBox : function(box){
33561         this.box = box;
33562         if(!this.collapsed){
33563             this.el.dom.style.left = box.x + "px";
33564             this.el.dom.style.top = box.y + "px";
33565             this.updateBody(box.width, box.height);
33566         }else{
33567             this.collapsedEl.dom.style.left = box.x + "px";
33568             this.collapsedEl.dom.style.top = box.y + "px";
33569             this.collapsedEl.setSize(box.width, box.height);
33570         }
33571         if(this.tabs){
33572             this.tabs.autoSizeTabs();
33573         }
33574     },
33575
33576     updateBody : function(w, h){
33577         if(w !== null){
33578             this.el.setWidth(w);
33579             w -= this.el.getBorderWidth("rl");
33580             if(this.config.adjustments){
33581                 w += this.config.adjustments[0];
33582             }
33583         }
33584         if(h !== null){
33585             this.el.setHeight(h);
33586             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33587             h -= this.el.getBorderWidth("tb");
33588             if(this.config.adjustments){
33589                 h += this.config.adjustments[1];
33590             }
33591             this.bodyEl.setHeight(h);
33592             if(this.tabs){
33593                 h = this.tabs.syncHeight(h);
33594             }
33595         }
33596         if(this.panelSize){
33597             w = w !== null ? w : this.panelSize.width;
33598             h = h !== null ? h : this.panelSize.height;
33599         }
33600         if(this.activePanel){
33601             var el = this.activePanel.getEl();
33602             w = w !== null ? w : el.getWidth();
33603             h = h !== null ? h : el.getHeight();
33604             this.panelSize = {width: w, height: h};
33605             this.activePanel.setSize(w, h);
33606         }
33607         if(Roo.isIE && this.tabs){
33608             this.tabs.el.repaint();
33609         }
33610     },
33611
33612     /**
33613      * Returns the container element for this region.
33614      * @return {Roo.Element}
33615      */
33616     getEl : function(){
33617         return this.el;
33618     },
33619
33620     /**
33621      * Hides this region.
33622      */
33623     hide : function(){
33624         if(!this.collapsed){
33625             this.el.dom.style.left = "-2000px";
33626             this.el.hide();
33627         }else{
33628             this.collapsedEl.dom.style.left = "-2000px";
33629             this.collapsedEl.hide();
33630         }
33631         this.visible = false;
33632         this.fireEvent("visibilitychange", this, false);
33633     },
33634
33635     /**
33636      * Shows this region if it was previously hidden.
33637      */
33638     show : function(){
33639         if(!this.collapsed){
33640             this.el.show();
33641         }else{
33642             this.collapsedEl.show();
33643         }
33644         this.visible = true;
33645         this.fireEvent("visibilitychange", this, true);
33646     },
33647
33648     closeClicked : function(){
33649         if(this.activePanel){
33650             this.remove(this.activePanel);
33651         }
33652     },
33653
33654     collapseClick : function(e){
33655         if(this.isSlid){
33656            e.stopPropagation();
33657            this.slideIn();
33658         }else{
33659            e.stopPropagation();
33660            this.slideOut();
33661         }
33662     },
33663
33664     /**
33665      * Collapses this region.
33666      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33667      */
33668     collapse : function(skipAnim){
33669         if(this.collapsed) return;
33670         this.collapsed = true;
33671         if(this.split){
33672             this.split.el.hide();
33673         }
33674         if(this.config.animate && skipAnim !== true){
33675             this.fireEvent("invalidated", this);
33676             this.animateCollapse();
33677         }else{
33678             this.el.setLocation(-20000,-20000);
33679             this.el.hide();
33680             this.collapsedEl.show();
33681             this.fireEvent("collapsed", this);
33682             this.fireEvent("invalidated", this);
33683         }
33684     },
33685
33686     animateCollapse : function(){
33687         // overridden
33688     },
33689
33690     /**
33691      * Expands this region if it was previously collapsed.
33692      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33693      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33694      */
33695     expand : function(e, skipAnim){
33696         if(e) e.stopPropagation();
33697         if(!this.collapsed || this.el.hasActiveFx()) return;
33698         if(this.isSlid){
33699             this.afterSlideIn();
33700             skipAnim = true;
33701         }
33702         this.collapsed = false;
33703         if(this.config.animate && skipAnim !== true){
33704             this.animateExpand();
33705         }else{
33706             this.el.show();
33707             if(this.split){
33708                 this.split.el.show();
33709             }
33710             this.collapsedEl.setLocation(-2000,-2000);
33711             this.collapsedEl.hide();
33712             this.fireEvent("invalidated", this);
33713             this.fireEvent("expanded", this);
33714         }
33715     },
33716
33717     animateExpand : function(){
33718         // overridden
33719     },
33720
33721     initTabs : function()
33722     {
33723         this.bodyEl.setStyle("overflow", "hidden");
33724         var ts = new Roo.TabPanel(
33725                 this.bodyEl.dom,
33726                 {
33727                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33728                     disableTooltips: this.config.disableTabTips,
33729                     toolbar : this.config.toolbar
33730                 }
33731         );
33732         if(this.config.hideTabs){
33733             ts.stripWrap.setDisplayed(false);
33734         }
33735         this.tabs = ts;
33736         ts.resizeTabs = this.config.resizeTabs === true;
33737         ts.minTabWidth = this.config.minTabWidth || 40;
33738         ts.maxTabWidth = this.config.maxTabWidth || 250;
33739         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33740         ts.monitorResize = false;
33741         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33742         ts.bodyEl.addClass('x-layout-tabs-body');
33743         this.panels.each(this.initPanelAsTab, this);
33744     },
33745
33746     initPanelAsTab : function(panel){
33747         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33748                     this.config.closeOnTab && panel.isClosable());
33749         if(panel.tabTip !== undefined){
33750             ti.setTooltip(panel.tabTip);
33751         }
33752         ti.on("activate", function(){
33753               this.setActivePanel(panel);
33754         }, this);
33755         if(this.config.closeOnTab){
33756             ti.on("beforeclose", function(t, e){
33757                 e.cancel = true;
33758                 this.remove(panel);
33759             }, this);
33760         }
33761         return ti;
33762     },
33763
33764     updatePanelTitle : function(panel, title){
33765         if(this.activePanel == panel){
33766             this.updateTitle(title);
33767         }
33768         if(this.tabs){
33769             var ti = this.tabs.getTab(panel.getEl().id);
33770             ti.setText(title);
33771             if(panel.tabTip !== undefined){
33772                 ti.setTooltip(panel.tabTip);
33773             }
33774         }
33775     },
33776
33777     updateTitle : function(title){
33778         if(this.titleTextEl && !this.config.title){
33779             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33780         }
33781     },
33782
33783     setActivePanel : function(panel){
33784         panel = this.getPanel(panel);
33785         if(this.activePanel && this.activePanel != panel){
33786             this.activePanel.setActiveState(false);
33787         }
33788         this.activePanel = panel;
33789         panel.setActiveState(true);
33790         if(this.panelSize){
33791             panel.setSize(this.panelSize.width, this.panelSize.height);
33792         }
33793         if(this.closeBtn){
33794             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33795         }
33796         this.updateTitle(panel.getTitle());
33797         if(this.tabs){
33798             this.fireEvent("invalidated", this);
33799         }
33800         this.fireEvent("panelactivated", this, panel);
33801     },
33802
33803     /**
33804      * Shows the specified panel.
33805      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33806      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33807      */
33808     showPanel : function(panel){
33809         if(panel = this.getPanel(panel)){
33810             if(this.tabs){
33811                 var tab = this.tabs.getTab(panel.getEl().id);
33812                 if(tab.isHidden()){
33813                     this.tabs.unhideTab(tab.id);
33814                 }
33815                 tab.activate();
33816             }else{
33817                 this.setActivePanel(panel);
33818             }
33819         }
33820         return panel;
33821     },
33822
33823     /**
33824      * Get the active panel for this region.
33825      * @return {Roo.ContentPanel} The active panel or null
33826      */
33827     getActivePanel : function(){
33828         return this.activePanel;
33829     },
33830
33831     validateVisibility : function(){
33832         if(this.panels.getCount() < 1){
33833             this.updateTitle("&#160;");
33834             this.closeBtn.hide();
33835             this.hide();
33836         }else{
33837             if(!this.isVisible()){
33838                 this.show();
33839             }
33840         }
33841     },
33842
33843     /**
33844      * Adds the passed ContentPanel(s) to this region.
33845      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33846      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33847      */
33848     add : function(panel){
33849         if(arguments.length > 1){
33850             for(var i = 0, len = arguments.length; i < len; i++) {
33851                 this.add(arguments[i]);
33852             }
33853             return null;
33854         }
33855         if(this.hasPanel(panel)){
33856             this.showPanel(panel);
33857             return panel;
33858         }
33859         panel.setRegion(this);
33860         this.panels.add(panel);
33861         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33862             this.bodyEl.dom.appendChild(panel.getEl().dom);
33863             if(panel.background !== true){
33864                 this.setActivePanel(panel);
33865             }
33866             this.fireEvent("paneladded", this, panel);
33867             return panel;
33868         }
33869         if(!this.tabs){
33870             this.initTabs();
33871         }else{
33872             this.initPanelAsTab(panel);
33873         }
33874         if(panel.background !== true){
33875             this.tabs.activate(panel.getEl().id);
33876         }
33877         this.fireEvent("paneladded", this, panel);
33878         return panel;
33879     },
33880
33881     /**
33882      * Hides the tab for the specified panel.
33883      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33884      */
33885     hidePanel : function(panel){
33886         if(this.tabs && (panel = this.getPanel(panel))){
33887             this.tabs.hideTab(panel.getEl().id);
33888         }
33889     },
33890
33891     /**
33892      * Unhides the tab for a previously hidden panel.
33893      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33894      */
33895     unhidePanel : function(panel){
33896         if(this.tabs && (panel = this.getPanel(panel))){
33897             this.tabs.unhideTab(panel.getEl().id);
33898         }
33899     },
33900
33901     clearPanels : function(){
33902         while(this.panels.getCount() > 0){
33903              this.remove(this.panels.first());
33904         }
33905     },
33906
33907     /**
33908      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33909      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33910      * @param {Boolean} preservePanel Overrides the config preservePanel option
33911      * @return {Roo.ContentPanel} The panel that was removed
33912      */
33913     remove : function(panel, preservePanel){
33914         panel = this.getPanel(panel);
33915         if(!panel){
33916             return null;
33917         }
33918         var e = {};
33919         this.fireEvent("beforeremove", this, panel, e);
33920         if(e.cancel === true){
33921             return null;
33922         }
33923         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33924         var panelId = panel.getId();
33925         this.panels.removeKey(panelId);
33926         if(preservePanel){
33927             document.body.appendChild(panel.getEl().dom);
33928         }
33929         if(this.tabs){
33930             this.tabs.removeTab(panel.getEl().id);
33931         }else if (!preservePanel){
33932             this.bodyEl.dom.removeChild(panel.getEl().dom);
33933         }
33934         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33935             var p = this.panels.first();
33936             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33937             tempEl.appendChild(p.getEl().dom);
33938             this.bodyEl.update("");
33939             this.bodyEl.dom.appendChild(p.getEl().dom);
33940             tempEl = null;
33941             this.updateTitle(p.getTitle());
33942             this.tabs = null;
33943             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33944             this.setActivePanel(p);
33945         }
33946         panel.setRegion(null);
33947         if(this.activePanel == panel){
33948             this.activePanel = null;
33949         }
33950         if(this.config.autoDestroy !== false && preservePanel !== true){
33951             try{panel.destroy();}catch(e){}
33952         }
33953         this.fireEvent("panelremoved", this, panel);
33954         return panel;
33955     },
33956
33957     /**
33958      * Returns the TabPanel component used by this region
33959      * @return {Roo.TabPanel}
33960      */
33961     getTabs : function(){
33962         return this.tabs;
33963     },
33964
33965     createTool : function(parentEl, className){
33966         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33967             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33968         btn.addClassOnOver("x-layout-tools-button-over");
33969         return btn;
33970     }
33971 });/*
33972  * Based on:
33973  * Ext JS Library 1.1.1
33974  * Copyright(c) 2006-2007, Ext JS, LLC.
33975  *
33976  * Originally Released Under LGPL - original licence link has changed is not relivant.
33977  *
33978  * Fork - LGPL
33979  * <script type="text/javascript">
33980  */
33981  
33982
33983
33984 /**
33985  * @class Roo.SplitLayoutRegion
33986  * @extends Roo.LayoutRegion
33987  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33988  */
33989 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33990     this.cursor = cursor;
33991     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33992 };
33993
33994 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33995     splitTip : "Drag to resize.",
33996     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33997     useSplitTips : false,
33998
33999     applyConfig : function(config){
34000         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34001         if(config.split){
34002             if(!this.split){
34003                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34004                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34005                 /** The SplitBar for this region 
34006                 * @type Roo.SplitBar */
34007                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34008                 this.split.on("moved", this.onSplitMove, this);
34009                 this.split.useShim = config.useShim === true;
34010                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34011                 if(this.useSplitTips){
34012                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34013                 }
34014                 if(config.collapsible){
34015                     this.split.el.on("dblclick", this.collapse,  this);
34016                 }
34017             }
34018             if(typeof config.minSize != "undefined"){
34019                 this.split.minSize = config.minSize;
34020             }
34021             if(typeof config.maxSize != "undefined"){
34022                 this.split.maxSize = config.maxSize;
34023             }
34024             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34025                 this.hideSplitter();
34026             }
34027         }
34028     },
34029
34030     getHMaxSize : function(){
34031          var cmax = this.config.maxSize || 10000;
34032          var center = this.mgr.getRegion("center");
34033          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34034     },
34035
34036     getVMaxSize : function(){
34037          var cmax = this.config.maxSize || 10000;
34038          var center = this.mgr.getRegion("center");
34039          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34040     },
34041
34042     onSplitMove : function(split, newSize){
34043         this.fireEvent("resized", this, newSize);
34044     },
34045     
34046     /** 
34047      * Returns the {@link Roo.SplitBar} for this region.
34048      * @return {Roo.SplitBar}
34049      */
34050     getSplitBar : function(){
34051         return this.split;
34052     },
34053     
34054     hide : function(){
34055         this.hideSplitter();
34056         Roo.SplitLayoutRegion.superclass.hide.call(this);
34057     },
34058
34059     hideSplitter : function(){
34060         if(this.split){
34061             this.split.el.setLocation(-2000,-2000);
34062             this.split.el.hide();
34063         }
34064     },
34065
34066     show : function(){
34067         if(this.split){
34068             this.split.el.show();
34069         }
34070         Roo.SplitLayoutRegion.superclass.show.call(this);
34071     },
34072     
34073     beforeSlide: function(){
34074         if(Roo.isGecko){// firefox overflow auto bug workaround
34075             this.bodyEl.clip();
34076             if(this.tabs) this.tabs.bodyEl.clip();
34077             if(this.activePanel){
34078                 this.activePanel.getEl().clip();
34079                 
34080                 if(this.activePanel.beforeSlide){
34081                     this.activePanel.beforeSlide();
34082                 }
34083             }
34084         }
34085     },
34086     
34087     afterSlide : function(){
34088         if(Roo.isGecko){// firefox overflow auto bug workaround
34089             this.bodyEl.unclip();
34090             if(this.tabs) this.tabs.bodyEl.unclip();
34091             if(this.activePanel){
34092                 this.activePanel.getEl().unclip();
34093                 if(this.activePanel.afterSlide){
34094                     this.activePanel.afterSlide();
34095                 }
34096             }
34097         }
34098     },
34099
34100     initAutoHide : function(){
34101         if(this.autoHide !== false){
34102             if(!this.autoHideHd){
34103                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34104                 this.autoHideHd = {
34105                     "mouseout": function(e){
34106                         if(!e.within(this.el, true)){
34107                             st.delay(500);
34108                         }
34109                     },
34110                     "mouseover" : function(e){
34111                         st.cancel();
34112                     },
34113                     scope : this
34114                 };
34115             }
34116             this.el.on(this.autoHideHd);
34117         }
34118     },
34119
34120     clearAutoHide : function(){
34121         if(this.autoHide !== false){
34122             this.el.un("mouseout", this.autoHideHd.mouseout);
34123             this.el.un("mouseover", this.autoHideHd.mouseover);
34124         }
34125     },
34126
34127     clearMonitor : function(){
34128         Roo.get(document).un("click", this.slideInIf, this);
34129     },
34130
34131     // these names are backwards but not changed for compat
34132     slideOut : function(){
34133         if(this.isSlid || this.el.hasActiveFx()){
34134             return;
34135         }
34136         this.isSlid = true;
34137         if(this.collapseBtn){
34138             this.collapseBtn.hide();
34139         }
34140         this.closeBtnState = this.closeBtn.getStyle('display');
34141         this.closeBtn.hide();
34142         if(this.stickBtn){
34143             this.stickBtn.show();
34144         }
34145         this.el.show();
34146         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34147         this.beforeSlide();
34148         this.el.setStyle("z-index", 10001);
34149         this.el.slideIn(this.getSlideAnchor(), {
34150             callback: function(){
34151                 this.afterSlide();
34152                 this.initAutoHide();
34153                 Roo.get(document).on("click", this.slideInIf, this);
34154                 this.fireEvent("slideshow", this);
34155             },
34156             scope: this,
34157             block: true
34158         });
34159     },
34160
34161     afterSlideIn : function(){
34162         this.clearAutoHide();
34163         this.isSlid = false;
34164         this.clearMonitor();
34165         this.el.setStyle("z-index", "");
34166         if(this.collapseBtn){
34167             this.collapseBtn.show();
34168         }
34169         this.closeBtn.setStyle('display', this.closeBtnState);
34170         if(this.stickBtn){
34171             this.stickBtn.hide();
34172         }
34173         this.fireEvent("slidehide", this);
34174     },
34175
34176     slideIn : function(cb){
34177         if(!this.isSlid || this.el.hasActiveFx()){
34178             Roo.callback(cb);
34179             return;
34180         }
34181         this.isSlid = false;
34182         this.beforeSlide();
34183         this.el.slideOut(this.getSlideAnchor(), {
34184             callback: function(){
34185                 this.el.setLeftTop(-10000, -10000);
34186                 this.afterSlide();
34187                 this.afterSlideIn();
34188                 Roo.callback(cb);
34189             },
34190             scope: this,
34191             block: true
34192         });
34193     },
34194     
34195     slideInIf : function(e){
34196         if(!e.within(this.el)){
34197             this.slideIn();
34198         }
34199     },
34200
34201     animateCollapse : function(){
34202         this.beforeSlide();
34203         this.el.setStyle("z-index", 20000);
34204         var anchor = this.getSlideAnchor();
34205         this.el.slideOut(anchor, {
34206             callback : function(){
34207                 this.el.setStyle("z-index", "");
34208                 this.collapsedEl.slideIn(anchor, {duration:.3});
34209                 this.afterSlide();
34210                 this.el.setLocation(-10000,-10000);
34211                 this.el.hide();
34212                 this.fireEvent("collapsed", this);
34213             },
34214             scope: this,
34215             block: true
34216         });
34217     },
34218
34219     animateExpand : function(){
34220         this.beforeSlide();
34221         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34222         this.el.setStyle("z-index", 20000);
34223         this.collapsedEl.hide({
34224             duration:.1
34225         });
34226         this.el.slideIn(this.getSlideAnchor(), {
34227             callback : function(){
34228                 this.el.setStyle("z-index", "");
34229                 this.afterSlide();
34230                 if(this.split){
34231                     this.split.el.show();
34232                 }
34233                 this.fireEvent("invalidated", this);
34234                 this.fireEvent("expanded", this);
34235             },
34236             scope: this,
34237             block: true
34238         });
34239     },
34240
34241     anchors : {
34242         "west" : "left",
34243         "east" : "right",
34244         "north" : "top",
34245         "south" : "bottom"
34246     },
34247
34248     sanchors : {
34249         "west" : "l",
34250         "east" : "r",
34251         "north" : "t",
34252         "south" : "b"
34253     },
34254
34255     canchors : {
34256         "west" : "tl-tr",
34257         "east" : "tr-tl",
34258         "north" : "tl-bl",
34259         "south" : "bl-tl"
34260     },
34261
34262     getAnchor : function(){
34263         return this.anchors[this.position];
34264     },
34265
34266     getCollapseAnchor : function(){
34267         return this.canchors[this.position];
34268     },
34269
34270     getSlideAnchor : function(){
34271         return this.sanchors[this.position];
34272     },
34273
34274     getAlignAdj : function(){
34275         var cm = this.cmargins;
34276         switch(this.position){
34277             case "west":
34278                 return [0, 0];
34279             break;
34280             case "east":
34281                 return [0, 0];
34282             break;
34283             case "north":
34284                 return [0, 0];
34285             break;
34286             case "south":
34287                 return [0, 0];
34288             break;
34289         }
34290     },
34291
34292     getExpandAdj : function(){
34293         var c = this.collapsedEl, cm = this.cmargins;
34294         switch(this.position){
34295             case "west":
34296                 return [-(cm.right+c.getWidth()+cm.left), 0];
34297             break;
34298             case "east":
34299                 return [cm.right+c.getWidth()+cm.left, 0];
34300             break;
34301             case "north":
34302                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34303             break;
34304             case "south":
34305                 return [0, cm.top+cm.bottom+c.getHeight()];
34306             break;
34307         }
34308     }
34309 });/*
34310  * Based on:
34311  * Ext JS Library 1.1.1
34312  * Copyright(c) 2006-2007, Ext JS, LLC.
34313  *
34314  * Originally Released Under LGPL - original licence link has changed is not relivant.
34315  *
34316  * Fork - LGPL
34317  * <script type="text/javascript">
34318  */
34319 /*
34320  * These classes are private internal classes
34321  */
34322 Roo.CenterLayoutRegion = function(mgr, config){
34323     Roo.LayoutRegion.call(this, mgr, config, "center");
34324     this.visible = true;
34325     this.minWidth = config.minWidth || 20;
34326     this.minHeight = config.minHeight || 20;
34327 };
34328
34329 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34330     hide : function(){
34331         // center panel can't be hidden
34332     },
34333     
34334     show : function(){
34335         // center panel can't be hidden
34336     },
34337     
34338     getMinWidth: function(){
34339         return this.minWidth;
34340     },
34341     
34342     getMinHeight: function(){
34343         return this.minHeight;
34344     }
34345 });
34346
34347
34348 Roo.NorthLayoutRegion = function(mgr, config){
34349     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34350     if(this.split){
34351         this.split.placement = Roo.SplitBar.TOP;
34352         this.split.orientation = Roo.SplitBar.VERTICAL;
34353         this.split.el.addClass("x-layout-split-v");
34354     }
34355     var size = config.initialSize || config.height;
34356     if(typeof size != "undefined"){
34357         this.el.setHeight(size);
34358     }
34359 };
34360 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34361     orientation: Roo.SplitBar.VERTICAL,
34362     getBox : function(){
34363         if(this.collapsed){
34364             return this.collapsedEl.getBox();
34365         }
34366         var box = this.el.getBox();
34367         if(this.split){
34368             box.height += this.split.el.getHeight();
34369         }
34370         return box;
34371     },
34372     
34373     updateBox : function(box){
34374         if(this.split && !this.collapsed){
34375             box.height -= this.split.el.getHeight();
34376             this.split.el.setLeft(box.x);
34377             this.split.el.setTop(box.y+box.height);
34378             this.split.el.setWidth(box.width);
34379         }
34380         if(this.collapsed){
34381             this.updateBody(box.width, null);
34382         }
34383         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34384     }
34385 });
34386
34387 Roo.SouthLayoutRegion = function(mgr, config){
34388     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34389     if(this.split){
34390         this.split.placement = Roo.SplitBar.BOTTOM;
34391         this.split.orientation = Roo.SplitBar.VERTICAL;
34392         this.split.el.addClass("x-layout-split-v");
34393     }
34394     var size = config.initialSize || config.height;
34395     if(typeof size != "undefined"){
34396         this.el.setHeight(size);
34397     }
34398 };
34399 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34400     orientation: Roo.SplitBar.VERTICAL,
34401     getBox : function(){
34402         if(this.collapsed){
34403             return this.collapsedEl.getBox();
34404         }
34405         var box = this.el.getBox();
34406         if(this.split){
34407             var sh = this.split.el.getHeight();
34408             box.height += sh;
34409             box.y -= sh;
34410         }
34411         return box;
34412     },
34413     
34414     updateBox : function(box){
34415         if(this.split && !this.collapsed){
34416             var sh = this.split.el.getHeight();
34417             box.height -= sh;
34418             box.y += sh;
34419             this.split.el.setLeft(box.x);
34420             this.split.el.setTop(box.y-sh);
34421             this.split.el.setWidth(box.width);
34422         }
34423         if(this.collapsed){
34424             this.updateBody(box.width, null);
34425         }
34426         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34427     }
34428 });
34429
34430 Roo.EastLayoutRegion = function(mgr, config){
34431     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34432     if(this.split){
34433         this.split.placement = Roo.SplitBar.RIGHT;
34434         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34435         this.split.el.addClass("x-layout-split-h");
34436     }
34437     var size = config.initialSize || config.width;
34438     if(typeof size != "undefined"){
34439         this.el.setWidth(size);
34440     }
34441 };
34442 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34443     orientation: Roo.SplitBar.HORIZONTAL,
34444     getBox : function(){
34445         if(this.collapsed){
34446             return this.collapsedEl.getBox();
34447         }
34448         var box = this.el.getBox();
34449         if(this.split){
34450             var sw = this.split.el.getWidth();
34451             box.width += sw;
34452             box.x -= sw;
34453         }
34454         return box;
34455     },
34456
34457     updateBox : function(box){
34458         if(this.split && !this.collapsed){
34459             var sw = this.split.el.getWidth();
34460             box.width -= sw;
34461             this.split.el.setLeft(box.x);
34462             this.split.el.setTop(box.y);
34463             this.split.el.setHeight(box.height);
34464             box.x += sw;
34465         }
34466         if(this.collapsed){
34467             this.updateBody(null, box.height);
34468         }
34469         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34470     }
34471 });
34472
34473 Roo.WestLayoutRegion = function(mgr, config){
34474     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34475     if(this.split){
34476         this.split.placement = Roo.SplitBar.LEFT;
34477         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34478         this.split.el.addClass("x-layout-split-h");
34479     }
34480     var size = config.initialSize || config.width;
34481     if(typeof size != "undefined"){
34482         this.el.setWidth(size);
34483     }
34484 };
34485 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34486     orientation: Roo.SplitBar.HORIZONTAL,
34487     getBox : function(){
34488         if(this.collapsed){
34489             return this.collapsedEl.getBox();
34490         }
34491         var box = this.el.getBox();
34492         if(this.split){
34493             box.width += this.split.el.getWidth();
34494         }
34495         return box;
34496     },
34497     
34498     updateBox : function(box){
34499         if(this.split && !this.collapsed){
34500             var sw = this.split.el.getWidth();
34501             box.width -= sw;
34502             this.split.el.setLeft(box.x+box.width);
34503             this.split.el.setTop(box.y);
34504             this.split.el.setHeight(box.height);
34505         }
34506         if(this.collapsed){
34507             this.updateBody(null, box.height);
34508         }
34509         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34510     }
34511 });
34512 /*
34513  * Based on:
34514  * Ext JS Library 1.1.1
34515  * Copyright(c) 2006-2007, Ext JS, LLC.
34516  *
34517  * Originally Released Under LGPL - original licence link has changed is not relivant.
34518  *
34519  * Fork - LGPL
34520  * <script type="text/javascript">
34521  */
34522  
34523  
34524 /*
34525  * Private internal class for reading and applying state
34526  */
34527 Roo.LayoutStateManager = function(layout){
34528      // default empty state
34529      this.state = {
34530         north: {},
34531         south: {},
34532         east: {},
34533         west: {}       
34534     };
34535 };
34536
34537 Roo.LayoutStateManager.prototype = {
34538     init : function(layout, provider){
34539         this.provider = provider;
34540         var state = provider.get(layout.id+"-layout-state");
34541         if(state){
34542             var wasUpdating = layout.isUpdating();
34543             if(!wasUpdating){
34544                 layout.beginUpdate();
34545             }
34546             for(var key in state){
34547                 if(typeof state[key] != "function"){
34548                     var rstate = state[key];
34549                     var r = layout.getRegion(key);
34550                     if(r && rstate){
34551                         if(rstate.size){
34552                             r.resizeTo(rstate.size);
34553                         }
34554                         if(rstate.collapsed == true){
34555                             r.collapse(true);
34556                         }else{
34557                             r.expand(null, true);
34558                         }
34559                     }
34560                 }
34561             }
34562             if(!wasUpdating){
34563                 layout.endUpdate();
34564             }
34565             this.state = state; 
34566         }
34567         this.layout = layout;
34568         layout.on("regionresized", this.onRegionResized, this);
34569         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34570         layout.on("regionexpanded", this.onRegionExpanded, this);
34571     },
34572     
34573     storeState : function(){
34574         this.provider.set(this.layout.id+"-layout-state", this.state);
34575     },
34576     
34577     onRegionResized : function(region, newSize){
34578         this.state[region.getPosition()].size = newSize;
34579         this.storeState();
34580     },
34581     
34582     onRegionCollapsed : function(region){
34583         this.state[region.getPosition()].collapsed = true;
34584         this.storeState();
34585     },
34586     
34587     onRegionExpanded : function(region){
34588         this.state[region.getPosition()].collapsed = false;
34589         this.storeState();
34590     }
34591 };/*
34592  * Based on:
34593  * Ext JS Library 1.1.1
34594  * Copyright(c) 2006-2007, Ext JS, LLC.
34595  *
34596  * Originally Released Under LGPL - original licence link has changed is not relivant.
34597  *
34598  * Fork - LGPL
34599  * <script type="text/javascript">
34600  */
34601 /**
34602  * @class Roo.ContentPanel
34603  * @extends Roo.util.Observable
34604  * A basic ContentPanel element.
34605  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34606  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34607  * @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
34608  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34609  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34610  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34611  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34612  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34613  * @cfg {String} title          The title for this panel
34614  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34615  * @cfg {String} url            Calls {@link #setUrl} with this value
34616  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34617  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34618  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34619  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34620
34621  * @constructor
34622  * Create a new ContentPanel.
34623  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34624  * @param {String/Object} config A string to set only the title or a config object
34625  * @param {String} content (optional) Set the HTML content for this panel
34626  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34627  */
34628 Roo.ContentPanel = function(el, config, content){
34629     
34630      
34631     /*
34632     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34633         config = el;
34634         el = Roo.id();
34635     }
34636     if (config && config.parentLayout) { 
34637         el = config.parentLayout.el.createChild(); 
34638     }
34639     */
34640     if(el.autoCreate){ // xtype is available if this is called from factory
34641         config = el;
34642         el = Roo.id();
34643     }
34644     this.el = Roo.get(el);
34645     if(!this.el && config && config.autoCreate){
34646         if(typeof config.autoCreate == "object"){
34647             if(!config.autoCreate.id){
34648                 config.autoCreate.id = config.id||el;
34649             }
34650             this.el = Roo.DomHelper.append(document.body,
34651                         config.autoCreate, true);
34652         }else{
34653             this.el = Roo.DomHelper.append(document.body,
34654                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34655         }
34656     }
34657     this.closable = false;
34658     this.loaded = false;
34659     this.active = false;
34660     if(typeof config == "string"){
34661         this.title = config;
34662     }else{
34663         Roo.apply(this, config);
34664     }
34665     
34666     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34667         this.wrapEl = this.el.wrap();
34668         this.toolbar.container = this.el.insertSibling(false, 'before');
34669         this.toolbar = new Roo.Toolbar(this.toolbar);
34670     }
34671     
34672     // xtype created footer. - not sure if will work as we normally have to render first..
34673     if (this.footer && !this.footer.el && this.footer.xtype) {
34674         if (!this.wrapEl) {
34675             this.wrapEl = this.el.wrap();
34676         }
34677     
34678         this.footer.container = this.wrapEl.createChild();
34679          
34680         this.footer = Roo.factory(this.footer, Roo);
34681         
34682     }
34683     
34684     if(this.resizeEl){
34685         this.resizeEl = Roo.get(this.resizeEl, true);
34686     }else{
34687         this.resizeEl = this.el;
34688     }
34689     // handle view.xtype
34690     
34691  
34692     
34693     
34694     this.addEvents({
34695         /**
34696          * @event activate
34697          * Fires when this panel is activated. 
34698          * @param {Roo.ContentPanel} this
34699          */
34700         "activate" : true,
34701         /**
34702          * @event deactivate
34703          * Fires when this panel is activated. 
34704          * @param {Roo.ContentPanel} this
34705          */
34706         "deactivate" : true,
34707
34708         /**
34709          * @event resize
34710          * Fires when this panel is resized if fitToFrame is true.
34711          * @param {Roo.ContentPanel} this
34712          * @param {Number} width The width after any component adjustments
34713          * @param {Number} height The height after any component adjustments
34714          */
34715         "resize" : true,
34716         
34717          /**
34718          * @event render
34719          * Fires when this tab is created
34720          * @param {Roo.ContentPanel} this
34721          */
34722         "render" : true
34723         
34724         
34725         
34726     });
34727     
34728
34729     
34730     
34731     if(this.autoScroll){
34732         this.resizeEl.setStyle("overflow", "auto");
34733     } else {
34734         // fix randome scrolling
34735         this.el.on('scroll', function() {
34736             Roo.log('fix random scolling');
34737             this.scrollTo('top',0); 
34738         });
34739     }
34740     content = content || this.content;
34741     if(content){
34742         this.setContent(content);
34743     }
34744     if(config && config.url){
34745         this.setUrl(this.url, this.params, this.loadOnce);
34746     }
34747     
34748     
34749     
34750     Roo.ContentPanel.superclass.constructor.call(this);
34751     
34752     if (this.view && typeof(this.view.xtype) != 'undefined') {
34753         this.view.el = this.el.appendChild(document.createElement("div"));
34754         this.view = Roo.factory(this.view); 
34755         this.view.render  &&  this.view.render(false, '');  
34756     }
34757     
34758     
34759     this.fireEvent('render', this);
34760 };
34761
34762 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34763     tabTip:'',
34764     setRegion : function(region){
34765         this.region = region;
34766         if(region){
34767            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34768         }else{
34769            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34770         } 
34771     },
34772     
34773     /**
34774      * Returns the toolbar for this Panel if one was configured. 
34775      * @return {Roo.Toolbar} 
34776      */
34777     getToolbar : function(){
34778         return this.toolbar;
34779     },
34780     
34781     setActiveState : function(active){
34782         this.active = active;
34783         if(!active){
34784             this.fireEvent("deactivate", this);
34785         }else{
34786             this.fireEvent("activate", this);
34787         }
34788     },
34789     /**
34790      * Updates this panel's element
34791      * @param {String} content The new content
34792      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34793     */
34794     setContent : function(content, loadScripts){
34795         this.el.update(content, loadScripts);
34796     },
34797
34798     ignoreResize : function(w, h){
34799         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34800             return true;
34801         }else{
34802             this.lastSize = {width: w, height: h};
34803             return false;
34804         }
34805     },
34806     /**
34807      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34808      * @return {Roo.UpdateManager} The UpdateManager
34809      */
34810     getUpdateManager : function(){
34811         return this.el.getUpdateManager();
34812     },
34813      /**
34814      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34815      * @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:
34816 <pre><code>
34817 panel.load({
34818     url: "your-url.php",
34819     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34820     callback: yourFunction,
34821     scope: yourObject, //(optional scope)
34822     discardUrl: false,
34823     nocache: false,
34824     text: "Loading...",
34825     timeout: 30,
34826     scripts: false
34827 });
34828 </code></pre>
34829      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34830      * 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.
34831      * @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}
34832      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34833      * @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.
34834      * @return {Roo.ContentPanel} this
34835      */
34836     load : function(){
34837         var um = this.el.getUpdateManager();
34838         um.update.apply(um, arguments);
34839         return this;
34840     },
34841
34842
34843     /**
34844      * 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.
34845      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34846      * @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)
34847      * @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)
34848      * @return {Roo.UpdateManager} The UpdateManager
34849      */
34850     setUrl : function(url, params, loadOnce){
34851         if(this.refreshDelegate){
34852             this.removeListener("activate", this.refreshDelegate);
34853         }
34854         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34855         this.on("activate", this.refreshDelegate);
34856         return this.el.getUpdateManager();
34857     },
34858     
34859     _handleRefresh : function(url, params, loadOnce){
34860         if(!loadOnce || !this.loaded){
34861             var updater = this.el.getUpdateManager();
34862             updater.update(url, params, this._setLoaded.createDelegate(this));
34863         }
34864     },
34865     
34866     _setLoaded : function(){
34867         this.loaded = true;
34868     }, 
34869     
34870     /**
34871      * Returns this panel's id
34872      * @return {String} 
34873      */
34874     getId : function(){
34875         return this.el.id;
34876     },
34877     
34878     /** 
34879      * Returns this panel's element - used by regiosn to add.
34880      * @return {Roo.Element} 
34881      */
34882     getEl : function(){
34883         return this.wrapEl || this.el;
34884     },
34885     
34886     adjustForComponents : function(width, height)
34887     {
34888         //Roo.log('adjustForComponents ');
34889         if(this.resizeEl != this.el){
34890             width -= this.el.getFrameWidth('lr');
34891             height -= this.el.getFrameWidth('tb');
34892         }
34893         if(this.toolbar){
34894             var te = this.toolbar.getEl();
34895             height -= te.getHeight();
34896             te.setWidth(width);
34897         }
34898         if(this.footer){
34899             var te = this.footer.getEl();
34900             Roo.log("footer:" + te.getHeight());
34901             
34902             height -= te.getHeight();
34903             te.setWidth(width);
34904         }
34905         
34906         
34907         if(this.adjustments){
34908             width += this.adjustments[0];
34909             height += this.adjustments[1];
34910         }
34911         return {"width": width, "height": height};
34912     },
34913     
34914     setSize : function(width, height){
34915         if(this.fitToFrame && !this.ignoreResize(width, height)){
34916             if(this.fitContainer && this.resizeEl != this.el){
34917                 this.el.setSize(width, height);
34918             }
34919             var size = this.adjustForComponents(width, height);
34920             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34921             this.fireEvent('resize', this, size.width, size.height);
34922         }
34923     },
34924     
34925     /**
34926      * Returns this panel's title
34927      * @return {String} 
34928      */
34929     getTitle : function(){
34930         return this.title;
34931     },
34932     
34933     /**
34934      * Set this panel's title
34935      * @param {String} title
34936      */
34937     setTitle : function(title){
34938         this.title = title;
34939         if(this.region){
34940             this.region.updatePanelTitle(this, title);
34941         }
34942     },
34943     
34944     /**
34945      * Returns true is this panel was configured to be closable
34946      * @return {Boolean} 
34947      */
34948     isClosable : function(){
34949         return this.closable;
34950     },
34951     
34952     beforeSlide : function(){
34953         this.el.clip();
34954         this.resizeEl.clip();
34955     },
34956     
34957     afterSlide : function(){
34958         this.el.unclip();
34959         this.resizeEl.unclip();
34960     },
34961     
34962     /**
34963      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34964      *   Will fail silently if the {@link #setUrl} method has not been called.
34965      *   This does not activate the panel, just updates its content.
34966      */
34967     refresh : function(){
34968         if(this.refreshDelegate){
34969            this.loaded = false;
34970            this.refreshDelegate();
34971         }
34972     },
34973     
34974     /**
34975      * Destroys this panel
34976      */
34977     destroy : function(){
34978         this.el.removeAllListeners();
34979         var tempEl = document.createElement("span");
34980         tempEl.appendChild(this.el.dom);
34981         tempEl.innerHTML = "";
34982         this.el.remove();
34983         this.el = null;
34984     },
34985     
34986     /**
34987      * form - if the content panel contains a form - this is a reference to it.
34988      * @type {Roo.form.Form}
34989      */
34990     form : false,
34991     /**
34992      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34993      *    This contains a reference to it.
34994      * @type {Roo.View}
34995      */
34996     view : false,
34997     
34998       /**
34999      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35000      * <pre><code>
35001
35002 layout.addxtype({
35003        xtype : 'Form',
35004        items: [ .... ]
35005    }
35006 );
35007
35008 </code></pre>
35009      * @param {Object} cfg Xtype definition of item to add.
35010      */
35011     
35012     addxtype : function(cfg) {
35013         // add form..
35014         if (cfg.xtype.match(/^Form$/)) {
35015             
35016             var el;
35017             //if (this.footer) {
35018             //    el = this.footer.container.insertSibling(false, 'before');
35019             //} else {
35020                 el = this.el.createChild();
35021             //}
35022
35023             this.form = new  Roo.form.Form(cfg);
35024             
35025             
35026             if ( this.form.allItems.length) this.form.render(el.dom);
35027             return this.form;
35028         }
35029         // should only have one of theses..
35030         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35031             // views.. should not be just added - used named prop 'view''
35032             
35033             cfg.el = this.el.appendChild(document.createElement("div"));
35034             // factory?
35035             
35036             var ret = new Roo.factory(cfg);
35037              
35038              ret.render && ret.render(false, ''); // render blank..
35039             this.view = ret;
35040             return ret;
35041         }
35042         return false;
35043     }
35044 });
35045
35046 /**
35047  * @class Roo.GridPanel
35048  * @extends Roo.ContentPanel
35049  * @constructor
35050  * Create a new GridPanel.
35051  * @param {Roo.grid.Grid} grid The grid for this panel
35052  * @param {String/Object} config A string to set only the panel's title, or a config object
35053  */
35054 Roo.GridPanel = function(grid, config){
35055     
35056   
35057     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35058         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35059         
35060     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35061     
35062     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35063     
35064     if(this.toolbar){
35065         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35066     }
35067     // xtype created footer. - not sure if will work as we normally have to render first..
35068     if (this.footer && !this.footer.el && this.footer.xtype) {
35069         
35070         this.footer.container = this.grid.getView().getFooterPanel(true);
35071         this.footer.dataSource = this.grid.dataSource;
35072         this.footer = Roo.factory(this.footer, Roo);
35073         
35074     }
35075     
35076     grid.monitorWindowResize = false; // turn off autosizing
35077     grid.autoHeight = false;
35078     grid.autoWidth = false;
35079     this.grid = grid;
35080     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35081 };
35082
35083 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35084     getId : function(){
35085         return this.grid.id;
35086     },
35087     
35088     /**
35089      * Returns the grid for this panel
35090      * @return {Roo.grid.Grid} 
35091      */
35092     getGrid : function(){
35093         return this.grid;    
35094     },
35095     
35096     setSize : function(width, height){
35097         if(!this.ignoreResize(width, height)){
35098             var grid = this.grid;
35099             var size = this.adjustForComponents(width, height);
35100             grid.getGridEl().setSize(size.width, size.height);
35101             grid.autoSize();
35102         }
35103     },
35104     
35105     beforeSlide : function(){
35106         this.grid.getView().scroller.clip();
35107     },
35108     
35109     afterSlide : function(){
35110         this.grid.getView().scroller.unclip();
35111     },
35112     
35113     destroy : function(){
35114         this.grid.destroy();
35115         delete this.grid;
35116         Roo.GridPanel.superclass.destroy.call(this); 
35117     }
35118 });
35119
35120
35121 /**
35122  * @class Roo.NestedLayoutPanel
35123  * @extends Roo.ContentPanel
35124  * @constructor
35125  * Create a new NestedLayoutPanel.
35126  * 
35127  * 
35128  * @param {Roo.BorderLayout} layout The layout for this panel
35129  * @param {String/Object} config A string to set only the title or a config object
35130  */
35131 Roo.NestedLayoutPanel = function(layout, config)
35132 {
35133     // construct with only one argument..
35134     /* FIXME - implement nicer consturctors
35135     if (layout.layout) {
35136         config = layout;
35137         layout = config.layout;
35138         delete config.layout;
35139     }
35140     if (layout.xtype && !layout.getEl) {
35141         // then layout needs constructing..
35142         layout = Roo.factory(layout, Roo);
35143     }
35144     */
35145     
35146     
35147     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35148     
35149     layout.monitorWindowResize = false; // turn off autosizing
35150     this.layout = layout;
35151     this.layout.getEl().addClass("x-layout-nested-layout");
35152     
35153     
35154     
35155     
35156 };
35157
35158 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35159
35160     setSize : function(width, height){
35161         if(!this.ignoreResize(width, height)){
35162             var size = this.adjustForComponents(width, height);
35163             var el = this.layout.getEl();
35164             el.setSize(size.width, size.height);
35165             var touch = el.dom.offsetWidth;
35166             this.layout.layout();
35167             // ie requires a double layout on the first pass
35168             if(Roo.isIE && !this.initialized){
35169                 this.initialized = true;
35170                 this.layout.layout();
35171             }
35172         }
35173     },
35174     
35175     // activate all subpanels if not currently active..
35176     
35177     setActiveState : function(active){
35178         this.active = active;
35179         if(!active){
35180             this.fireEvent("deactivate", this);
35181             return;
35182         }
35183         
35184         this.fireEvent("activate", this);
35185         // not sure if this should happen before or after..
35186         if (!this.layout) {
35187             return; // should not happen..
35188         }
35189         var reg = false;
35190         for (var r in this.layout.regions) {
35191             reg = this.layout.getRegion(r);
35192             if (reg.getActivePanel()) {
35193                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35194                 reg.setActivePanel(reg.getActivePanel());
35195                 continue;
35196             }
35197             if (!reg.panels.length) {
35198                 continue;
35199             }
35200             reg.showPanel(reg.getPanel(0));
35201         }
35202         
35203         
35204         
35205         
35206     },
35207     
35208     /**
35209      * Returns the nested BorderLayout for this panel
35210      * @return {Roo.BorderLayout} 
35211      */
35212     getLayout : function(){
35213         return this.layout;
35214     },
35215     
35216      /**
35217      * Adds a xtype elements to the layout of the nested panel
35218      * <pre><code>
35219
35220 panel.addxtype({
35221        xtype : 'ContentPanel',
35222        region: 'west',
35223        items: [ .... ]
35224    }
35225 );
35226
35227 panel.addxtype({
35228         xtype : 'NestedLayoutPanel',
35229         region: 'west',
35230         layout: {
35231            center: { },
35232            west: { }   
35233         },
35234         items : [ ... list of content panels or nested layout panels.. ]
35235    }
35236 );
35237 </code></pre>
35238      * @param {Object} cfg Xtype definition of item to add.
35239      */
35240     addxtype : function(cfg) {
35241         return this.layout.addxtype(cfg);
35242     
35243     }
35244 });
35245
35246 Roo.ScrollPanel = function(el, config, content){
35247     config = config || {};
35248     config.fitToFrame = true;
35249     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35250     
35251     this.el.dom.style.overflow = "hidden";
35252     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35253     this.el.removeClass("x-layout-inactive-content");
35254     this.el.on("mousewheel", this.onWheel, this);
35255
35256     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35257     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35258     up.unselectable(); down.unselectable();
35259     up.on("click", this.scrollUp, this);
35260     down.on("click", this.scrollDown, this);
35261     up.addClassOnOver("x-scroller-btn-over");
35262     down.addClassOnOver("x-scroller-btn-over");
35263     up.addClassOnClick("x-scroller-btn-click");
35264     down.addClassOnClick("x-scroller-btn-click");
35265     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35266
35267     this.resizeEl = this.el;
35268     this.el = wrap; this.up = up; this.down = down;
35269 };
35270
35271 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35272     increment : 100,
35273     wheelIncrement : 5,
35274     scrollUp : function(){
35275         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35276     },
35277
35278     scrollDown : function(){
35279         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35280     },
35281
35282     afterScroll : function(){
35283         var el = this.resizeEl;
35284         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35285         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35286         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35287     },
35288
35289     setSize : function(){
35290         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35291         this.afterScroll();
35292     },
35293
35294     onWheel : function(e){
35295         var d = e.getWheelDelta();
35296         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35297         this.afterScroll();
35298         e.stopEvent();
35299     },
35300
35301     setContent : function(content, loadScripts){
35302         this.resizeEl.update(content, loadScripts);
35303     }
35304
35305 });
35306
35307
35308
35309
35310
35311
35312
35313
35314
35315 /**
35316  * @class Roo.TreePanel
35317  * @extends Roo.ContentPanel
35318  * @constructor
35319  * Create a new TreePanel. - defaults to fit/scoll contents.
35320  * @param {String/Object} config A string to set only the panel's title, or a config object
35321  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35322  */
35323 Roo.TreePanel = function(config){
35324     var el = config.el;
35325     var tree = config.tree;
35326     delete config.tree; 
35327     delete config.el; // hopefull!
35328     
35329     // wrapper for IE7 strict & safari scroll issue
35330     
35331     var treeEl = el.createChild();
35332     config.resizeEl = treeEl;
35333     
35334     
35335     
35336     Roo.TreePanel.superclass.constructor.call(this, el, config);
35337  
35338  
35339     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35340     //console.log(tree);
35341     this.on('activate', function()
35342     {
35343         if (this.tree.rendered) {
35344             return;
35345         }
35346         //console.log('render tree');
35347         this.tree.render();
35348     });
35349     // this should not be needed.. - it's actually the 'el' that resizes?
35350     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35351     
35352     //this.on('resize',  function (cp, w, h) {
35353     //        this.tree.innerCt.setWidth(w);
35354     //        this.tree.innerCt.setHeight(h);
35355     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35356     //});
35357
35358         
35359     
35360 };
35361
35362 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35363     fitToFrame : true,
35364     autoScroll : true
35365 });
35366
35367
35368
35369
35370
35371
35372
35373
35374
35375
35376
35377 /*
35378  * Based on:
35379  * Ext JS Library 1.1.1
35380  * Copyright(c) 2006-2007, Ext JS, LLC.
35381  *
35382  * Originally Released Under LGPL - original licence link has changed is not relivant.
35383  *
35384  * Fork - LGPL
35385  * <script type="text/javascript">
35386  */
35387  
35388
35389 /**
35390  * @class Roo.ReaderLayout
35391  * @extends Roo.BorderLayout
35392  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35393  * center region containing two nested regions (a top one for a list view and one for item preview below),
35394  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35395  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35396  * expedites the setup of the overall layout and regions for this common application style.
35397  * Example:
35398  <pre><code>
35399 var reader = new Roo.ReaderLayout();
35400 var CP = Roo.ContentPanel;  // shortcut for adding
35401
35402 reader.beginUpdate();
35403 reader.add("north", new CP("north", "North"));
35404 reader.add("west", new CP("west", {title: "West"}));
35405 reader.add("east", new CP("east", {title: "East"}));
35406
35407 reader.regions.listView.add(new CP("listView", "List"));
35408 reader.regions.preview.add(new CP("preview", "Preview"));
35409 reader.endUpdate();
35410 </code></pre>
35411 * @constructor
35412 * Create a new ReaderLayout
35413 * @param {Object} config Configuration options
35414 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35415 * document.body if omitted)
35416 */
35417 Roo.ReaderLayout = function(config, renderTo){
35418     var c = config || {size:{}};
35419     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35420         north: c.north !== false ? Roo.apply({
35421             split:false,
35422             initialSize: 32,
35423             titlebar: false
35424         }, c.north) : false,
35425         west: c.west !== false ? Roo.apply({
35426             split:true,
35427             initialSize: 200,
35428             minSize: 175,
35429             maxSize: 400,
35430             titlebar: true,
35431             collapsible: true,
35432             animate: true,
35433             margins:{left:5,right:0,bottom:5,top:5},
35434             cmargins:{left:5,right:5,bottom:5,top:5}
35435         }, c.west) : false,
35436         east: c.east !== false ? Roo.apply({
35437             split:true,
35438             initialSize: 200,
35439             minSize: 175,
35440             maxSize: 400,
35441             titlebar: true,
35442             collapsible: true,
35443             animate: true,
35444             margins:{left:0,right:5,bottom:5,top:5},
35445             cmargins:{left:5,right:5,bottom:5,top:5}
35446         }, c.east) : false,
35447         center: Roo.apply({
35448             tabPosition: 'top',
35449             autoScroll:false,
35450             closeOnTab: true,
35451             titlebar:false,
35452             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35453         }, c.center)
35454     });
35455
35456     this.el.addClass('x-reader');
35457
35458     this.beginUpdate();
35459
35460     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35461         south: c.preview !== false ? Roo.apply({
35462             split:true,
35463             initialSize: 200,
35464             minSize: 100,
35465             autoScroll:true,
35466             collapsible:true,
35467             titlebar: true,
35468             cmargins:{top:5,left:0, right:0, bottom:0}
35469         }, c.preview) : false,
35470         center: Roo.apply({
35471             autoScroll:false,
35472             titlebar:false,
35473             minHeight:200
35474         }, c.listView)
35475     });
35476     this.add('center', new Roo.NestedLayoutPanel(inner,
35477             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35478
35479     this.endUpdate();
35480
35481     this.regions.preview = inner.getRegion('south');
35482     this.regions.listView = inner.getRegion('center');
35483 };
35484
35485 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35486  * Based on:
35487  * Ext JS Library 1.1.1
35488  * Copyright(c) 2006-2007, Ext JS, LLC.
35489  *
35490  * Originally Released Under LGPL - original licence link has changed is not relivant.
35491  *
35492  * Fork - LGPL
35493  * <script type="text/javascript">
35494  */
35495  
35496 /**
35497  * @class Roo.grid.Grid
35498  * @extends Roo.util.Observable
35499  * This class represents the primary interface of a component based grid control.
35500  * <br><br>Usage:<pre><code>
35501  var grid = new Roo.grid.Grid("my-container-id", {
35502      ds: myDataStore,
35503      cm: myColModel,
35504      selModel: mySelectionModel,
35505      autoSizeColumns: true,
35506      monitorWindowResize: false,
35507      trackMouseOver: true
35508  });
35509  // set any options
35510  grid.render();
35511  * </code></pre>
35512  * <b>Common Problems:</b><br/>
35513  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35514  * element will correct this<br/>
35515  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35516  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35517  * are unpredictable.<br/>
35518  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35519  * grid to calculate dimensions/offsets.<br/>
35520   * @constructor
35521  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35522  * The container MUST have some type of size defined for the grid to fill. The container will be
35523  * automatically set to position relative if it isn't already.
35524  * @param {Object} config A config object that sets properties on this grid.
35525  */
35526 Roo.grid.Grid = function(container, config){
35527         // initialize the container
35528         this.container = Roo.get(container);
35529         this.container.update("");
35530         this.container.setStyle("overflow", "hidden");
35531     this.container.addClass('x-grid-container');
35532
35533     this.id = this.container.id;
35534
35535     Roo.apply(this, config);
35536     // check and correct shorthanded configs
35537     if(this.ds){
35538         this.dataSource = this.ds;
35539         delete this.ds;
35540     }
35541     if(this.cm){
35542         this.colModel = this.cm;
35543         delete this.cm;
35544     }
35545     if(this.sm){
35546         this.selModel = this.sm;
35547         delete this.sm;
35548     }
35549
35550     if (this.selModel) {
35551         this.selModel = Roo.factory(this.selModel, Roo.grid);
35552         this.sm = this.selModel;
35553         this.sm.xmodule = this.xmodule || false;
35554     }
35555     if (typeof(this.colModel.config) == 'undefined') {
35556         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35557         this.cm = this.colModel;
35558         this.cm.xmodule = this.xmodule || false;
35559     }
35560     if (this.dataSource) {
35561         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35562         this.ds = this.dataSource;
35563         this.ds.xmodule = this.xmodule || false;
35564          
35565     }
35566     
35567     
35568     
35569     if(this.width){
35570         this.container.setWidth(this.width);
35571     }
35572
35573     if(this.height){
35574         this.container.setHeight(this.height);
35575     }
35576     /** @private */
35577         this.addEvents({
35578         // raw events
35579         /**
35580          * @event click
35581          * The raw click event for the entire grid.
35582          * @param {Roo.EventObject} e
35583          */
35584         "click" : true,
35585         /**
35586          * @event dblclick
35587          * The raw dblclick event for the entire grid.
35588          * @param {Roo.EventObject} e
35589          */
35590         "dblclick" : true,
35591         /**
35592          * @event contextmenu
35593          * The raw contextmenu event for the entire grid.
35594          * @param {Roo.EventObject} e
35595          */
35596         "contextmenu" : true,
35597         /**
35598          * @event mousedown
35599          * The raw mousedown event for the entire grid.
35600          * @param {Roo.EventObject} e
35601          */
35602         "mousedown" : true,
35603         /**
35604          * @event mouseup
35605          * The raw mouseup event for the entire grid.
35606          * @param {Roo.EventObject} e
35607          */
35608         "mouseup" : true,
35609         /**
35610          * @event mouseover
35611          * The raw mouseover event for the entire grid.
35612          * @param {Roo.EventObject} e
35613          */
35614         "mouseover" : true,
35615         /**
35616          * @event mouseout
35617          * The raw mouseout event for the entire grid.
35618          * @param {Roo.EventObject} e
35619          */
35620         "mouseout" : true,
35621         /**
35622          * @event keypress
35623          * The raw keypress event for the entire grid.
35624          * @param {Roo.EventObject} e
35625          */
35626         "keypress" : true,
35627         /**
35628          * @event keydown
35629          * The raw keydown event for the entire grid.
35630          * @param {Roo.EventObject} e
35631          */
35632         "keydown" : true,
35633
35634         // custom events
35635
35636         /**
35637          * @event cellclick
35638          * Fires when a cell is clicked
35639          * @param {Grid} this
35640          * @param {Number} rowIndex
35641          * @param {Number} columnIndex
35642          * @param {Roo.EventObject} e
35643          */
35644         "cellclick" : true,
35645         /**
35646          * @event celldblclick
35647          * Fires when a cell is double clicked
35648          * @param {Grid} this
35649          * @param {Number} rowIndex
35650          * @param {Number} columnIndex
35651          * @param {Roo.EventObject} e
35652          */
35653         "celldblclick" : true,
35654         /**
35655          * @event rowclick
35656          * Fires when a row is clicked
35657          * @param {Grid} this
35658          * @param {Number} rowIndex
35659          * @param {Roo.EventObject} e
35660          */
35661         "rowclick" : true,
35662         /**
35663          * @event rowdblclick
35664          * Fires when a row is double clicked
35665          * @param {Grid} this
35666          * @param {Number} rowIndex
35667          * @param {Roo.EventObject} e
35668          */
35669         "rowdblclick" : true,
35670         /**
35671          * @event headerclick
35672          * Fires when a header is clicked
35673          * @param {Grid} this
35674          * @param {Number} columnIndex
35675          * @param {Roo.EventObject} e
35676          */
35677         "headerclick" : true,
35678         /**
35679          * @event headerdblclick
35680          * Fires when a header cell is double clicked
35681          * @param {Grid} this
35682          * @param {Number} columnIndex
35683          * @param {Roo.EventObject} e
35684          */
35685         "headerdblclick" : true,
35686         /**
35687          * @event rowcontextmenu
35688          * Fires when a row is right clicked
35689          * @param {Grid} this
35690          * @param {Number} rowIndex
35691          * @param {Roo.EventObject} e
35692          */
35693         "rowcontextmenu" : true,
35694         /**
35695          * @event cellcontextmenu
35696          * Fires when a cell is right clicked
35697          * @param {Grid} this
35698          * @param {Number} rowIndex
35699          * @param {Number} cellIndex
35700          * @param {Roo.EventObject} e
35701          */
35702          "cellcontextmenu" : true,
35703         /**
35704          * @event headercontextmenu
35705          * Fires when a header is right clicked
35706          * @param {Grid} this
35707          * @param {Number} columnIndex
35708          * @param {Roo.EventObject} e
35709          */
35710         "headercontextmenu" : true,
35711         /**
35712          * @event bodyscroll
35713          * Fires when the body element is scrolled
35714          * @param {Number} scrollLeft
35715          * @param {Number} scrollTop
35716          */
35717         "bodyscroll" : true,
35718         /**
35719          * @event columnresize
35720          * Fires when the user resizes a column
35721          * @param {Number} columnIndex
35722          * @param {Number} newSize
35723          */
35724         "columnresize" : true,
35725         /**
35726          * @event columnmove
35727          * Fires when the user moves a column
35728          * @param {Number} oldIndex
35729          * @param {Number} newIndex
35730          */
35731         "columnmove" : true,
35732         /**
35733          * @event startdrag
35734          * Fires when row(s) start being dragged
35735          * @param {Grid} this
35736          * @param {Roo.GridDD} dd The drag drop object
35737          * @param {event} e The raw browser event
35738          */
35739         "startdrag" : true,
35740         /**
35741          * @event enddrag
35742          * Fires when a drag operation is complete
35743          * @param {Grid} this
35744          * @param {Roo.GridDD} dd The drag drop object
35745          * @param {event} e The raw browser event
35746          */
35747         "enddrag" : true,
35748         /**
35749          * @event dragdrop
35750          * Fires when dragged row(s) are dropped on a valid DD target
35751          * @param {Grid} this
35752          * @param {Roo.GridDD} dd The drag drop object
35753          * @param {String} targetId The target drag drop object
35754          * @param {event} e The raw browser event
35755          */
35756         "dragdrop" : true,
35757         /**
35758          * @event dragover
35759          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35760          * @param {Grid} this
35761          * @param {Roo.GridDD} dd The drag drop object
35762          * @param {String} targetId The target drag drop object
35763          * @param {event} e The raw browser event
35764          */
35765         "dragover" : true,
35766         /**
35767          * @event dragenter
35768          *  Fires when the dragged row(s) first cross another DD target while being dragged
35769          * @param {Grid} this
35770          * @param {Roo.GridDD} dd The drag drop object
35771          * @param {String} targetId The target drag drop object
35772          * @param {event} e The raw browser event
35773          */
35774         "dragenter" : true,
35775         /**
35776          * @event dragout
35777          * Fires when the dragged row(s) leave another DD target while being dragged
35778          * @param {Grid} this
35779          * @param {Roo.GridDD} dd The drag drop object
35780          * @param {String} targetId The target drag drop object
35781          * @param {event} e The raw browser event
35782          */
35783         "dragout" : true,
35784         /**
35785          * @event rowclass
35786          * Fires when a row is rendered, so you can change add a style to it.
35787          * @param {GridView} gridview   The grid view
35788          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35789          */
35790         'rowclass' : true,
35791
35792         /**
35793          * @event render
35794          * Fires when the grid is rendered
35795          * @param {Grid} grid
35796          */
35797         'render' : true
35798     });
35799
35800     Roo.grid.Grid.superclass.constructor.call(this);
35801 };
35802 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35803     
35804     /**
35805      * @cfg {String} ddGroup - drag drop group.
35806      */
35807
35808     /**
35809      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35810      */
35811     minColumnWidth : 25,
35812
35813     /**
35814      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35815      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35816      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35817      */
35818     autoSizeColumns : false,
35819
35820     /**
35821      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35822      */
35823     autoSizeHeaders : true,
35824
35825     /**
35826      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35827      */
35828     monitorWindowResize : true,
35829
35830     /**
35831      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35832      * rows measured to get a columns size. Default is 0 (all rows).
35833      */
35834     maxRowsToMeasure : 0,
35835
35836     /**
35837      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35838      */
35839     trackMouseOver : true,
35840
35841     /**
35842     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35843     */
35844     
35845     /**
35846     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35847     */
35848     enableDragDrop : false,
35849     
35850     /**
35851     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35852     */
35853     enableColumnMove : true,
35854     
35855     /**
35856     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35857     */
35858     enableColumnHide : true,
35859     
35860     /**
35861     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35862     */
35863     enableRowHeightSync : false,
35864     
35865     /**
35866     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35867     */
35868     stripeRows : true,
35869     
35870     /**
35871     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35872     */
35873     autoHeight : false,
35874
35875     /**
35876      * @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.
35877      */
35878     autoExpandColumn : false,
35879
35880     /**
35881     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35882     * Default is 50.
35883     */
35884     autoExpandMin : 50,
35885
35886     /**
35887     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35888     */
35889     autoExpandMax : 1000,
35890
35891     /**
35892     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35893     */
35894     view : null,
35895
35896     /**
35897     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35898     */
35899     loadMask : false,
35900     /**
35901     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35902     */
35903     dropTarget: false,
35904     
35905    
35906     
35907     // private
35908     rendered : false,
35909
35910     /**
35911     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35912     * of a fixed width. Default is false.
35913     */
35914     /**
35915     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35916     */
35917     /**
35918      * Called once after all setup has been completed and the grid is ready to be rendered.
35919      * @return {Roo.grid.Grid} this
35920      */
35921     render : function()
35922     {
35923         var c = this.container;
35924         // try to detect autoHeight/width mode
35925         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35926             this.autoHeight = true;
35927         }
35928         var view = this.getView();
35929         view.init(this);
35930
35931         c.on("click", this.onClick, this);
35932         c.on("dblclick", this.onDblClick, this);
35933         c.on("contextmenu", this.onContextMenu, this);
35934         c.on("keydown", this.onKeyDown, this);
35935         if (Roo.isTouch) {
35936             c.on("touchstart", this.onTouchStart, this);
35937         }
35938
35939         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35940
35941         this.getSelectionModel().init(this);
35942
35943         view.render();
35944
35945         if(this.loadMask){
35946             this.loadMask = new Roo.LoadMask(this.container,
35947                     Roo.apply({store:this.dataSource}, this.loadMask));
35948         }
35949         
35950         
35951         if (this.toolbar && this.toolbar.xtype) {
35952             this.toolbar.container = this.getView().getHeaderPanel(true);
35953             this.toolbar = new Roo.Toolbar(this.toolbar);
35954         }
35955         if (this.footer && this.footer.xtype) {
35956             this.footer.dataSource = this.getDataSource();
35957             this.footer.container = this.getView().getFooterPanel(true);
35958             this.footer = Roo.factory(this.footer, Roo);
35959         }
35960         if (this.dropTarget && this.dropTarget.xtype) {
35961             delete this.dropTarget.xtype;
35962             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35963         }
35964         
35965         
35966         this.rendered = true;
35967         this.fireEvent('render', this);
35968         return this;
35969     },
35970
35971         /**
35972          * Reconfigures the grid to use a different Store and Column Model.
35973          * The View will be bound to the new objects and refreshed.
35974          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35975          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35976          */
35977     reconfigure : function(dataSource, colModel){
35978         if(this.loadMask){
35979             this.loadMask.destroy();
35980             this.loadMask = new Roo.LoadMask(this.container,
35981                     Roo.apply({store:dataSource}, this.loadMask));
35982         }
35983         this.view.bind(dataSource, colModel);
35984         this.dataSource = dataSource;
35985         this.colModel = colModel;
35986         this.view.refresh(true);
35987     },
35988
35989     // private
35990     onKeyDown : function(e){
35991         this.fireEvent("keydown", e);
35992     },
35993
35994     /**
35995      * Destroy this grid.
35996      * @param {Boolean} removeEl True to remove the element
35997      */
35998     destroy : function(removeEl, keepListeners){
35999         if(this.loadMask){
36000             this.loadMask.destroy();
36001         }
36002         var c = this.container;
36003         c.removeAllListeners();
36004         this.view.destroy();
36005         this.colModel.purgeListeners();
36006         if(!keepListeners){
36007             this.purgeListeners();
36008         }
36009         c.update("");
36010         if(removeEl === true){
36011             c.remove();
36012         }
36013     },
36014
36015     // private
36016     processEvent : function(name, e){
36017         // does this fire select???
36018         Roo.log('grid:processEvent '  + name);
36019         
36020         if (name != 'touchstart' ) {
36021             this.fireEvent(name, e);    
36022         }
36023         
36024         var t = e.getTarget();
36025         var v = this.view;
36026         var header = v.findHeaderIndex(t);
36027         if(header !== false){
36028             var ename = name == 'touchstart' ? 'click' : name;
36029              
36030             this.fireEvent("header" + ename, this, header, e);
36031         }else{
36032             var row = v.findRowIndex(t);
36033             var cell = v.findCellIndex(t);
36034             if (name == 'touchstart') {
36035                 // first touch is always a click.
36036                 // hopefull this happens after selection is updated.?
36037                 name = false;
36038                 
36039                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36040                     var cs = this.selModel.getSelectedCell();
36041                     if (row == cs[0] && cell == cs[1]){
36042                         name = 'dblclick';
36043                     }
36044                 }
36045                 if (typeof(this.selModel.getSelections) != 'undefined') {
36046                     var cs = this.selModel.getSelections();
36047                     var ds = this.dataSource;
36048                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36049                         name = 'dblclick';
36050                     }
36051                 }
36052                 if (!name) {
36053                     return;
36054                 }
36055             }
36056             
36057             
36058             if(row !== false){
36059                 this.fireEvent("row" + name, this, row, e);
36060                 if(cell !== false){
36061                     this.fireEvent("cell" + name, this, row, cell, e);
36062                 }
36063             }
36064         }
36065     },
36066
36067     // private
36068     onClick : function(e){
36069         this.processEvent("click", e);
36070     },
36071    // private
36072     onTouchStart : function(e){
36073         this.processEvent("touchstart", e);
36074     },
36075
36076     // private
36077     onContextMenu : function(e, t){
36078         this.processEvent("contextmenu", e);
36079     },
36080
36081     // private
36082     onDblClick : function(e){
36083         this.processEvent("dblclick", e);
36084     },
36085
36086     // private
36087     walkCells : function(row, col, step, fn, scope){
36088         var cm = this.colModel, clen = cm.getColumnCount();
36089         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36090         if(step < 0){
36091             if(col < 0){
36092                 row--;
36093                 first = false;
36094             }
36095             while(row >= 0){
36096                 if(!first){
36097                     col = clen-1;
36098                 }
36099                 first = false;
36100                 while(col >= 0){
36101                     if(fn.call(scope || this, row, col, cm) === true){
36102                         return [row, col];
36103                     }
36104                     col--;
36105                 }
36106                 row--;
36107             }
36108         } else {
36109             if(col >= clen){
36110                 row++;
36111                 first = false;
36112             }
36113             while(row < rlen){
36114                 if(!first){
36115                     col = 0;
36116                 }
36117                 first = false;
36118                 while(col < clen){
36119                     if(fn.call(scope || this, row, col, cm) === true){
36120                         return [row, col];
36121                     }
36122                     col++;
36123                 }
36124                 row++;
36125             }
36126         }
36127         return null;
36128     },
36129
36130     // private
36131     getSelections : function(){
36132         return this.selModel.getSelections();
36133     },
36134
36135     /**
36136      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36137      * but if manual update is required this method will initiate it.
36138      */
36139     autoSize : function(){
36140         if(this.rendered){
36141             this.view.layout();
36142             if(this.view.adjustForScroll){
36143                 this.view.adjustForScroll();
36144             }
36145         }
36146     },
36147
36148     /**
36149      * Returns the grid's underlying element.
36150      * @return {Element} The element
36151      */
36152     getGridEl : function(){
36153         return this.container;
36154     },
36155
36156     // private for compatibility, overridden by editor grid
36157     stopEditing : function(){},
36158
36159     /**
36160      * Returns the grid's SelectionModel.
36161      * @return {SelectionModel}
36162      */
36163     getSelectionModel : function(){
36164         if(!this.selModel){
36165             this.selModel = new Roo.grid.RowSelectionModel();
36166         }
36167         return this.selModel;
36168     },
36169
36170     /**
36171      * Returns the grid's DataSource.
36172      * @return {DataSource}
36173      */
36174     getDataSource : function(){
36175         return this.dataSource;
36176     },
36177
36178     /**
36179      * Returns the grid's ColumnModel.
36180      * @return {ColumnModel}
36181      */
36182     getColumnModel : function(){
36183         return this.colModel;
36184     },
36185
36186     /**
36187      * Returns the grid's GridView object.
36188      * @return {GridView}
36189      */
36190     getView : function(){
36191         if(!this.view){
36192             this.view = new Roo.grid.GridView(this.viewConfig);
36193         }
36194         return this.view;
36195     },
36196     /**
36197      * Called to get grid's drag proxy text, by default returns this.ddText.
36198      * @return {String}
36199      */
36200     getDragDropText : function(){
36201         var count = this.selModel.getCount();
36202         return String.format(this.ddText, count, count == 1 ? '' : 's');
36203     }
36204 });
36205 /**
36206  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36207  * %0 is replaced with the number of selected rows.
36208  * @type String
36209  */
36210 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36211  * Based on:
36212  * Ext JS Library 1.1.1
36213  * Copyright(c) 2006-2007, Ext JS, LLC.
36214  *
36215  * Originally Released Under LGPL - original licence link has changed is not relivant.
36216  *
36217  * Fork - LGPL
36218  * <script type="text/javascript">
36219  */
36220  
36221 Roo.grid.AbstractGridView = function(){
36222         this.grid = null;
36223         
36224         this.events = {
36225             "beforerowremoved" : true,
36226             "beforerowsinserted" : true,
36227             "beforerefresh" : true,
36228             "rowremoved" : true,
36229             "rowsinserted" : true,
36230             "rowupdated" : true,
36231             "refresh" : true
36232         };
36233     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36234 };
36235
36236 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36237     rowClass : "x-grid-row",
36238     cellClass : "x-grid-cell",
36239     tdClass : "x-grid-td",
36240     hdClass : "x-grid-hd",
36241     splitClass : "x-grid-hd-split",
36242     
36243     init: function(grid){
36244         this.grid = grid;
36245                 var cid = this.grid.getGridEl().id;
36246         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36247         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36248         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36249         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36250         },
36251         
36252     getColumnRenderers : function(){
36253         var renderers = [];
36254         var cm = this.grid.colModel;
36255         var colCount = cm.getColumnCount();
36256         for(var i = 0; i < colCount; i++){
36257             renderers[i] = cm.getRenderer(i);
36258         }
36259         return renderers;
36260     },
36261     
36262     getColumnIds : function(){
36263         var ids = [];
36264         var cm = this.grid.colModel;
36265         var colCount = cm.getColumnCount();
36266         for(var i = 0; i < colCount; i++){
36267             ids[i] = cm.getColumnId(i);
36268         }
36269         return ids;
36270     },
36271     
36272     getDataIndexes : function(){
36273         if(!this.indexMap){
36274             this.indexMap = this.buildIndexMap();
36275         }
36276         return this.indexMap.colToData;
36277     },
36278     
36279     getColumnIndexByDataIndex : function(dataIndex){
36280         if(!this.indexMap){
36281             this.indexMap = this.buildIndexMap();
36282         }
36283         return this.indexMap.dataToCol[dataIndex];
36284     },
36285     
36286     /**
36287      * Set a css style for a column dynamically. 
36288      * @param {Number} colIndex The index of the column
36289      * @param {String} name The css property name
36290      * @param {String} value The css value
36291      */
36292     setCSSStyle : function(colIndex, name, value){
36293         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36294         Roo.util.CSS.updateRule(selector, name, value);
36295     },
36296     
36297     generateRules : function(cm){
36298         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36299         Roo.util.CSS.removeStyleSheet(rulesId);
36300         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36301             var cid = cm.getColumnId(i);
36302             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36303                          this.tdSelector, cid, " {\n}\n",
36304                          this.hdSelector, cid, " {\n}\n",
36305                          this.splitSelector, cid, " {\n}\n");
36306         }
36307         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36308     }
36309 });/*
36310  * Based on:
36311  * Ext JS Library 1.1.1
36312  * Copyright(c) 2006-2007, Ext JS, LLC.
36313  *
36314  * Originally Released Under LGPL - original licence link has changed is not relivant.
36315  *
36316  * Fork - LGPL
36317  * <script type="text/javascript">
36318  */
36319
36320 // private
36321 // This is a support class used internally by the Grid components
36322 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36323     this.grid = grid;
36324     this.view = grid.getView();
36325     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36326     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36327     if(hd2){
36328         this.setHandleElId(Roo.id(hd));
36329         this.setOuterHandleElId(Roo.id(hd2));
36330     }
36331     this.scroll = false;
36332 };
36333 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36334     maxDragWidth: 120,
36335     getDragData : function(e){
36336         var t = Roo.lib.Event.getTarget(e);
36337         var h = this.view.findHeaderCell(t);
36338         if(h){
36339             return {ddel: h.firstChild, header:h};
36340         }
36341         return false;
36342     },
36343
36344     onInitDrag : function(e){
36345         this.view.headersDisabled = true;
36346         var clone = this.dragData.ddel.cloneNode(true);
36347         clone.id = Roo.id();
36348         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36349         this.proxy.update(clone);
36350         return true;
36351     },
36352
36353     afterValidDrop : function(){
36354         var v = this.view;
36355         setTimeout(function(){
36356             v.headersDisabled = false;
36357         }, 50);
36358     },
36359
36360     afterInvalidDrop : function(){
36361         var v = this.view;
36362         setTimeout(function(){
36363             v.headersDisabled = false;
36364         }, 50);
36365     }
36366 });
36367 /*
36368  * Based on:
36369  * Ext JS Library 1.1.1
36370  * Copyright(c) 2006-2007, Ext JS, LLC.
36371  *
36372  * Originally Released Under LGPL - original licence link has changed is not relivant.
36373  *
36374  * Fork - LGPL
36375  * <script type="text/javascript">
36376  */
36377 // private
36378 // This is a support class used internally by the Grid components
36379 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36380     this.grid = grid;
36381     this.view = grid.getView();
36382     // split the proxies so they don't interfere with mouse events
36383     this.proxyTop = Roo.DomHelper.append(document.body, {
36384         cls:"col-move-top", html:"&#160;"
36385     }, true);
36386     this.proxyBottom = Roo.DomHelper.append(document.body, {
36387         cls:"col-move-bottom", html:"&#160;"
36388     }, true);
36389     this.proxyTop.hide = this.proxyBottom.hide = function(){
36390         this.setLeftTop(-100,-100);
36391         this.setStyle("visibility", "hidden");
36392     };
36393     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36394     // temporarily disabled
36395     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36396     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36397 };
36398 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36399     proxyOffsets : [-4, -9],
36400     fly: Roo.Element.fly,
36401
36402     getTargetFromEvent : function(e){
36403         var t = Roo.lib.Event.getTarget(e);
36404         var cindex = this.view.findCellIndex(t);
36405         if(cindex !== false){
36406             return this.view.getHeaderCell(cindex);
36407         }
36408         return null;
36409     },
36410
36411     nextVisible : function(h){
36412         var v = this.view, cm = this.grid.colModel;
36413         h = h.nextSibling;
36414         while(h){
36415             if(!cm.isHidden(v.getCellIndex(h))){
36416                 return h;
36417             }
36418             h = h.nextSibling;
36419         }
36420         return null;
36421     },
36422
36423     prevVisible : function(h){
36424         var v = this.view, cm = this.grid.colModel;
36425         h = h.prevSibling;
36426         while(h){
36427             if(!cm.isHidden(v.getCellIndex(h))){
36428                 return h;
36429             }
36430             h = h.prevSibling;
36431         }
36432         return null;
36433     },
36434
36435     positionIndicator : function(h, n, e){
36436         var x = Roo.lib.Event.getPageX(e);
36437         var r = Roo.lib.Dom.getRegion(n.firstChild);
36438         var px, pt, py = r.top + this.proxyOffsets[1];
36439         if((r.right - x) <= (r.right-r.left)/2){
36440             px = r.right+this.view.borderWidth;
36441             pt = "after";
36442         }else{
36443             px = r.left;
36444             pt = "before";
36445         }
36446         var oldIndex = this.view.getCellIndex(h);
36447         var newIndex = this.view.getCellIndex(n);
36448
36449         if(this.grid.colModel.isFixed(newIndex)){
36450             return false;
36451         }
36452
36453         var locked = this.grid.colModel.isLocked(newIndex);
36454
36455         if(pt == "after"){
36456             newIndex++;
36457         }
36458         if(oldIndex < newIndex){
36459             newIndex--;
36460         }
36461         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36462             return false;
36463         }
36464         px +=  this.proxyOffsets[0];
36465         this.proxyTop.setLeftTop(px, py);
36466         this.proxyTop.show();
36467         if(!this.bottomOffset){
36468             this.bottomOffset = this.view.mainHd.getHeight();
36469         }
36470         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36471         this.proxyBottom.show();
36472         return pt;
36473     },
36474
36475     onNodeEnter : function(n, dd, e, data){
36476         if(data.header != n){
36477             this.positionIndicator(data.header, n, e);
36478         }
36479     },
36480
36481     onNodeOver : function(n, dd, e, data){
36482         var result = false;
36483         if(data.header != n){
36484             result = this.positionIndicator(data.header, n, e);
36485         }
36486         if(!result){
36487             this.proxyTop.hide();
36488             this.proxyBottom.hide();
36489         }
36490         return result ? this.dropAllowed : this.dropNotAllowed;
36491     },
36492
36493     onNodeOut : function(n, dd, e, data){
36494         this.proxyTop.hide();
36495         this.proxyBottom.hide();
36496     },
36497
36498     onNodeDrop : function(n, dd, e, data){
36499         var h = data.header;
36500         if(h != n){
36501             var cm = this.grid.colModel;
36502             var x = Roo.lib.Event.getPageX(e);
36503             var r = Roo.lib.Dom.getRegion(n.firstChild);
36504             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36505             var oldIndex = this.view.getCellIndex(h);
36506             var newIndex = this.view.getCellIndex(n);
36507             var locked = cm.isLocked(newIndex);
36508             if(pt == "after"){
36509                 newIndex++;
36510             }
36511             if(oldIndex < newIndex){
36512                 newIndex--;
36513             }
36514             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36515                 return false;
36516             }
36517             cm.setLocked(oldIndex, locked, true);
36518             cm.moveColumn(oldIndex, newIndex);
36519             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36520             return true;
36521         }
36522         return false;
36523     }
36524 });
36525 /*
36526  * Based on:
36527  * Ext JS Library 1.1.1
36528  * Copyright(c) 2006-2007, Ext JS, LLC.
36529  *
36530  * Originally Released Under LGPL - original licence link has changed is not relivant.
36531  *
36532  * Fork - LGPL
36533  * <script type="text/javascript">
36534  */
36535   
36536 /**
36537  * @class Roo.grid.GridView
36538  * @extends Roo.util.Observable
36539  *
36540  * @constructor
36541  * @param {Object} config
36542  */
36543 Roo.grid.GridView = function(config){
36544     Roo.grid.GridView.superclass.constructor.call(this);
36545     this.el = null;
36546
36547     Roo.apply(this, config);
36548 };
36549
36550 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36551
36552     unselectable :  'unselectable="on"',
36553     unselectableCls :  'x-unselectable',
36554     
36555     
36556     rowClass : "x-grid-row",
36557
36558     cellClass : "x-grid-col",
36559
36560     tdClass : "x-grid-td",
36561
36562     hdClass : "x-grid-hd",
36563
36564     splitClass : "x-grid-split",
36565
36566     sortClasses : ["sort-asc", "sort-desc"],
36567
36568     enableMoveAnim : false,
36569
36570     hlColor: "C3DAF9",
36571
36572     dh : Roo.DomHelper,
36573
36574     fly : Roo.Element.fly,
36575
36576     css : Roo.util.CSS,
36577
36578     borderWidth: 1,
36579
36580     splitOffset: 3,
36581
36582     scrollIncrement : 22,
36583
36584     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36585
36586     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36587
36588     bind : function(ds, cm){
36589         if(this.ds){
36590             this.ds.un("load", this.onLoad, this);
36591             this.ds.un("datachanged", this.onDataChange, this);
36592             this.ds.un("add", this.onAdd, this);
36593             this.ds.un("remove", this.onRemove, this);
36594             this.ds.un("update", this.onUpdate, this);
36595             this.ds.un("clear", this.onClear, this);
36596         }
36597         if(ds){
36598             ds.on("load", this.onLoad, this);
36599             ds.on("datachanged", this.onDataChange, this);
36600             ds.on("add", this.onAdd, this);
36601             ds.on("remove", this.onRemove, this);
36602             ds.on("update", this.onUpdate, this);
36603             ds.on("clear", this.onClear, this);
36604         }
36605         this.ds = ds;
36606
36607         if(this.cm){
36608             this.cm.un("widthchange", this.onColWidthChange, this);
36609             this.cm.un("headerchange", this.onHeaderChange, this);
36610             this.cm.un("hiddenchange", this.onHiddenChange, this);
36611             this.cm.un("columnmoved", this.onColumnMove, this);
36612             this.cm.un("columnlockchange", this.onColumnLock, this);
36613         }
36614         if(cm){
36615             this.generateRules(cm);
36616             cm.on("widthchange", this.onColWidthChange, this);
36617             cm.on("headerchange", this.onHeaderChange, this);
36618             cm.on("hiddenchange", this.onHiddenChange, this);
36619             cm.on("columnmoved", this.onColumnMove, this);
36620             cm.on("columnlockchange", this.onColumnLock, this);
36621         }
36622         this.cm = cm;
36623     },
36624
36625     init: function(grid){
36626         Roo.grid.GridView.superclass.init.call(this, grid);
36627
36628         this.bind(grid.dataSource, grid.colModel);
36629
36630         grid.on("headerclick", this.handleHeaderClick, this);
36631
36632         if(grid.trackMouseOver){
36633             grid.on("mouseover", this.onRowOver, this);
36634             grid.on("mouseout", this.onRowOut, this);
36635         }
36636         grid.cancelTextSelection = function(){};
36637         this.gridId = grid.id;
36638
36639         var tpls = this.templates || {};
36640
36641         if(!tpls.master){
36642             tpls.master = new Roo.Template(
36643                '<div class="x-grid" hidefocus="true">',
36644                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36645                   '<div class="x-grid-topbar"></div>',
36646                   '<div class="x-grid-scroller"><div></div></div>',
36647                   '<div class="x-grid-locked">',
36648                       '<div class="x-grid-header">{lockedHeader}</div>',
36649                       '<div class="x-grid-body">{lockedBody}</div>',
36650                   "</div>",
36651                   '<div class="x-grid-viewport">',
36652                       '<div class="x-grid-header">{header}</div>',
36653                       '<div class="x-grid-body">{body}</div>',
36654                   "</div>",
36655                   '<div class="x-grid-bottombar"></div>',
36656                  
36657                   '<div class="x-grid-resize-proxy">&#160;</div>',
36658                "</div>"
36659             );
36660             tpls.master.disableformats = true;
36661         }
36662
36663         if(!tpls.header){
36664             tpls.header = new Roo.Template(
36665                '<table border="0" cellspacing="0" cellpadding="0">',
36666                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36667                "</table>{splits}"
36668             );
36669             tpls.header.disableformats = true;
36670         }
36671         tpls.header.compile();
36672
36673         if(!tpls.hcell){
36674             tpls.hcell = new Roo.Template(
36675                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36676                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36677                 "</div></td>"
36678              );
36679              tpls.hcell.disableFormats = true;
36680         }
36681         tpls.hcell.compile();
36682
36683         if(!tpls.hsplit){
36684             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36685                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36686             tpls.hsplit.disableFormats = true;
36687         }
36688         tpls.hsplit.compile();
36689
36690         if(!tpls.body){
36691             tpls.body = new Roo.Template(
36692                '<table border="0" cellspacing="0" cellpadding="0">',
36693                "<tbody>{rows}</tbody>",
36694                "</table>"
36695             );
36696             tpls.body.disableFormats = true;
36697         }
36698         tpls.body.compile();
36699
36700         if(!tpls.row){
36701             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36702             tpls.row.disableFormats = true;
36703         }
36704         tpls.row.compile();
36705
36706         if(!tpls.cell){
36707             tpls.cell = new Roo.Template(
36708                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36709                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36710                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36711                 "</td>"
36712             );
36713             tpls.cell.disableFormats = true;
36714         }
36715         tpls.cell.compile();
36716
36717         this.templates = tpls;
36718     },
36719
36720     // remap these for backwards compat
36721     onColWidthChange : function(){
36722         this.updateColumns.apply(this, arguments);
36723     },
36724     onHeaderChange : function(){
36725         this.updateHeaders.apply(this, arguments);
36726     }, 
36727     onHiddenChange : function(){
36728         this.handleHiddenChange.apply(this, arguments);
36729     },
36730     onColumnMove : function(){
36731         this.handleColumnMove.apply(this, arguments);
36732     },
36733     onColumnLock : function(){
36734         this.handleLockChange.apply(this, arguments);
36735     },
36736
36737     onDataChange : function(){
36738         this.refresh();
36739         this.updateHeaderSortState();
36740     },
36741
36742     onClear : function(){
36743         this.refresh();
36744     },
36745
36746     onUpdate : function(ds, record){
36747         this.refreshRow(record);
36748     },
36749
36750     refreshRow : function(record){
36751         var ds = this.ds, index;
36752         if(typeof record == 'number'){
36753             index = record;
36754             record = ds.getAt(index);
36755         }else{
36756             index = ds.indexOf(record);
36757         }
36758         this.insertRows(ds, index, index, true);
36759         this.onRemove(ds, record, index+1, true);
36760         this.syncRowHeights(index, index);
36761         this.layout();
36762         this.fireEvent("rowupdated", this, index, record);
36763     },
36764
36765     onAdd : function(ds, records, index){
36766         this.insertRows(ds, index, index + (records.length-1));
36767     },
36768
36769     onRemove : function(ds, record, index, isUpdate){
36770         if(isUpdate !== true){
36771             this.fireEvent("beforerowremoved", this, index, record);
36772         }
36773         var bt = this.getBodyTable(), lt = this.getLockedTable();
36774         if(bt.rows[index]){
36775             bt.firstChild.removeChild(bt.rows[index]);
36776         }
36777         if(lt.rows[index]){
36778             lt.firstChild.removeChild(lt.rows[index]);
36779         }
36780         if(isUpdate !== true){
36781             this.stripeRows(index);
36782             this.syncRowHeights(index, index);
36783             this.layout();
36784             this.fireEvent("rowremoved", this, index, record);
36785         }
36786     },
36787
36788     onLoad : function(){
36789         this.scrollToTop();
36790     },
36791
36792     /**
36793      * Scrolls the grid to the top
36794      */
36795     scrollToTop : function(){
36796         if(this.scroller){
36797             this.scroller.dom.scrollTop = 0;
36798             this.syncScroll();
36799         }
36800     },
36801
36802     /**
36803      * Gets a panel in the header of the grid that can be used for toolbars etc.
36804      * After modifying the contents of this panel a call to grid.autoSize() may be
36805      * required to register any changes in size.
36806      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36807      * @return Roo.Element
36808      */
36809     getHeaderPanel : function(doShow){
36810         if(doShow){
36811             this.headerPanel.show();
36812         }
36813         return this.headerPanel;
36814     },
36815
36816     /**
36817      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36818      * After modifying the contents of this panel a call to grid.autoSize() may be
36819      * required to register any changes in size.
36820      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36821      * @return Roo.Element
36822      */
36823     getFooterPanel : function(doShow){
36824         if(doShow){
36825             this.footerPanel.show();
36826         }
36827         return this.footerPanel;
36828     },
36829
36830     initElements : function(){
36831         var E = Roo.Element;
36832         var el = this.grid.getGridEl().dom.firstChild;
36833         var cs = el.childNodes;
36834
36835         this.el = new E(el);
36836         
36837          this.focusEl = new E(el.firstChild);
36838         this.focusEl.swallowEvent("click", true);
36839         
36840         this.headerPanel = new E(cs[1]);
36841         this.headerPanel.enableDisplayMode("block");
36842
36843         this.scroller = new E(cs[2]);
36844         this.scrollSizer = new E(this.scroller.dom.firstChild);
36845
36846         this.lockedWrap = new E(cs[3]);
36847         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36848         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36849
36850         this.mainWrap = new E(cs[4]);
36851         this.mainHd = new E(this.mainWrap.dom.firstChild);
36852         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36853
36854         this.footerPanel = new E(cs[5]);
36855         this.footerPanel.enableDisplayMode("block");
36856
36857         this.resizeProxy = new E(cs[6]);
36858
36859         this.headerSelector = String.format(
36860            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36861            this.lockedHd.id, this.mainHd.id
36862         );
36863
36864         this.splitterSelector = String.format(
36865            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36866            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36867         );
36868     },
36869     idToCssName : function(s)
36870     {
36871         return s.replace(/[^a-z0-9]+/ig, '-');
36872     },
36873
36874     getHeaderCell : function(index){
36875         return Roo.DomQuery.select(this.headerSelector)[index];
36876     },
36877
36878     getHeaderCellMeasure : function(index){
36879         return this.getHeaderCell(index).firstChild;
36880     },
36881
36882     getHeaderCellText : function(index){
36883         return this.getHeaderCell(index).firstChild.firstChild;
36884     },
36885
36886     getLockedTable : function(){
36887         return this.lockedBody.dom.firstChild;
36888     },
36889
36890     getBodyTable : function(){
36891         return this.mainBody.dom.firstChild;
36892     },
36893
36894     getLockedRow : function(index){
36895         return this.getLockedTable().rows[index];
36896     },
36897
36898     getRow : function(index){
36899         return this.getBodyTable().rows[index];
36900     },
36901
36902     getRowComposite : function(index){
36903         if(!this.rowEl){
36904             this.rowEl = new Roo.CompositeElementLite();
36905         }
36906         var els = [], lrow, mrow;
36907         if(lrow = this.getLockedRow(index)){
36908             els.push(lrow);
36909         }
36910         if(mrow = this.getRow(index)){
36911             els.push(mrow);
36912         }
36913         this.rowEl.elements = els;
36914         return this.rowEl;
36915     },
36916     /**
36917      * Gets the 'td' of the cell
36918      * 
36919      * @param {Integer} rowIndex row to select
36920      * @param {Integer} colIndex column to select
36921      * 
36922      * @return {Object} 
36923      */
36924     getCell : function(rowIndex, colIndex){
36925         var locked = this.cm.getLockedCount();
36926         var source;
36927         if(colIndex < locked){
36928             source = this.lockedBody.dom.firstChild;
36929         }else{
36930             source = this.mainBody.dom.firstChild;
36931             colIndex -= locked;
36932         }
36933         return source.rows[rowIndex].childNodes[colIndex];
36934     },
36935
36936     getCellText : function(rowIndex, colIndex){
36937         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36938     },
36939
36940     getCellBox : function(cell){
36941         var b = this.fly(cell).getBox();
36942         if(Roo.isOpera){ // opera fails to report the Y
36943             b.y = cell.offsetTop + this.mainBody.getY();
36944         }
36945         return b;
36946     },
36947
36948     getCellIndex : function(cell){
36949         var id = String(cell.className).match(this.cellRE);
36950         if(id){
36951             return parseInt(id[1], 10);
36952         }
36953         return 0;
36954     },
36955
36956     findHeaderIndex : function(n){
36957         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36958         return r ? this.getCellIndex(r) : false;
36959     },
36960
36961     findHeaderCell : function(n){
36962         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36963         return r ? r : false;
36964     },
36965
36966     findRowIndex : function(n){
36967         if(!n){
36968             return false;
36969         }
36970         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36971         return r ? r.rowIndex : false;
36972     },
36973
36974     findCellIndex : function(node){
36975         var stop = this.el.dom;
36976         while(node && node != stop){
36977             if(this.findRE.test(node.className)){
36978                 return this.getCellIndex(node);
36979             }
36980             node = node.parentNode;
36981         }
36982         return false;
36983     },
36984
36985     getColumnId : function(index){
36986         return this.cm.getColumnId(index);
36987     },
36988
36989     getSplitters : function()
36990     {
36991         if(this.splitterSelector){
36992            return Roo.DomQuery.select(this.splitterSelector);
36993         }else{
36994             return null;
36995       }
36996     },
36997
36998     getSplitter : function(index){
36999         return this.getSplitters()[index];
37000     },
37001
37002     onRowOver : function(e, t){
37003         var row;
37004         if((row = this.findRowIndex(t)) !== false){
37005             this.getRowComposite(row).addClass("x-grid-row-over");
37006         }
37007     },
37008
37009     onRowOut : function(e, t){
37010         var row;
37011         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37012             this.getRowComposite(row).removeClass("x-grid-row-over");
37013         }
37014     },
37015
37016     renderHeaders : function(){
37017         var cm = this.cm;
37018         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37019         var cb = [], lb = [], sb = [], lsb = [], p = {};
37020         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37021             p.cellId = "x-grid-hd-0-" + i;
37022             p.splitId = "x-grid-csplit-0-" + i;
37023             p.id = cm.getColumnId(i);
37024             p.title = cm.getColumnTooltip(i) || "";
37025             p.value = cm.getColumnHeader(i) || "";
37026             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37027             if(!cm.isLocked(i)){
37028                 cb[cb.length] = ct.apply(p);
37029                 sb[sb.length] = st.apply(p);
37030             }else{
37031                 lb[lb.length] = ct.apply(p);
37032                 lsb[lsb.length] = st.apply(p);
37033             }
37034         }
37035         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37036                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37037     },
37038
37039     updateHeaders : function(){
37040         var html = this.renderHeaders();
37041         this.lockedHd.update(html[0]);
37042         this.mainHd.update(html[1]);
37043     },
37044
37045     /**
37046      * Focuses the specified row.
37047      * @param {Number} row The row index
37048      */
37049     focusRow : function(row)
37050     {
37051         //Roo.log('GridView.focusRow');
37052         var x = this.scroller.dom.scrollLeft;
37053         this.focusCell(row, 0, false);
37054         this.scroller.dom.scrollLeft = x;
37055     },
37056
37057     /**
37058      * Focuses the specified cell.
37059      * @param {Number} row The row index
37060      * @param {Number} col The column index
37061      * @param {Boolean} hscroll false to disable horizontal scrolling
37062      */
37063     focusCell : function(row, col, hscroll)
37064     {
37065         //Roo.log('GridView.focusCell');
37066         var el = this.ensureVisible(row, col, hscroll);
37067         this.focusEl.alignTo(el, "tl-tl");
37068         if(Roo.isGecko){
37069             this.focusEl.focus();
37070         }else{
37071             this.focusEl.focus.defer(1, this.focusEl);
37072         }
37073     },
37074
37075     /**
37076      * Scrolls the specified cell into view
37077      * @param {Number} row The row index
37078      * @param {Number} col The column index
37079      * @param {Boolean} hscroll false to disable horizontal scrolling
37080      */
37081     ensureVisible : function(row, col, hscroll)
37082     {
37083         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37084         //return null; //disable for testing.
37085         if(typeof row != "number"){
37086             row = row.rowIndex;
37087         }
37088         if(row < 0 && row >= this.ds.getCount()){
37089             return  null;
37090         }
37091         col = (col !== undefined ? col : 0);
37092         var cm = this.grid.colModel;
37093         while(cm.isHidden(col)){
37094             col++;
37095         }
37096
37097         var el = this.getCell(row, col);
37098         if(!el){
37099             return null;
37100         }
37101         var c = this.scroller.dom;
37102
37103         var ctop = parseInt(el.offsetTop, 10);
37104         var cleft = parseInt(el.offsetLeft, 10);
37105         var cbot = ctop + el.offsetHeight;
37106         var cright = cleft + el.offsetWidth;
37107         
37108         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37109         var stop = parseInt(c.scrollTop, 10);
37110         var sleft = parseInt(c.scrollLeft, 10);
37111         var sbot = stop + ch;
37112         var sright = sleft + c.clientWidth;
37113         /*
37114         Roo.log('GridView.ensureVisible:' +
37115                 ' ctop:' + ctop +
37116                 ' c.clientHeight:' + c.clientHeight +
37117                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37118                 ' stop:' + stop +
37119                 ' cbot:' + cbot +
37120                 ' sbot:' + sbot +
37121                 ' ch:' + ch  
37122                 );
37123         */
37124         if(ctop < stop){
37125              c.scrollTop = ctop;
37126             //Roo.log("set scrolltop to ctop DISABLE?");
37127         }else if(cbot > sbot){
37128             //Roo.log("set scrolltop to cbot-ch");
37129             c.scrollTop = cbot-ch;
37130         }
37131         
37132         if(hscroll !== false){
37133             if(cleft < sleft){
37134                 c.scrollLeft = cleft;
37135             }else if(cright > sright){
37136                 c.scrollLeft = cright-c.clientWidth;
37137             }
37138         }
37139          
37140         return el;
37141     },
37142
37143     updateColumns : function(){
37144         this.grid.stopEditing();
37145         var cm = this.grid.colModel, colIds = this.getColumnIds();
37146         //var totalWidth = cm.getTotalWidth();
37147         var pos = 0;
37148         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37149             //if(cm.isHidden(i)) continue;
37150             var w = cm.getColumnWidth(i);
37151             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37152             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37153         }
37154         this.updateSplitters();
37155     },
37156
37157     generateRules : function(cm){
37158         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37159         Roo.util.CSS.removeStyleSheet(rulesId);
37160         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37161             var cid = cm.getColumnId(i);
37162             var align = '';
37163             if(cm.config[i].align){
37164                 align = 'text-align:'+cm.config[i].align+';';
37165             }
37166             var hidden = '';
37167             if(cm.isHidden(i)){
37168                 hidden = 'display:none;';
37169             }
37170             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37171             ruleBuf.push(
37172                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37173                     this.hdSelector, cid, " {\n", align, width, "}\n",
37174                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37175                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37176         }
37177         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37178     },
37179
37180     updateSplitters : function(){
37181         var cm = this.cm, s = this.getSplitters();
37182         if(s){ // splitters not created yet
37183             var pos = 0, locked = true;
37184             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37185                 if(cm.isHidden(i)) continue;
37186                 var w = cm.getColumnWidth(i); // make sure it's a number
37187                 if(!cm.isLocked(i) && locked){
37188                     pos = 0;
37189                     locked = false;
37190                 }
37191                 pos += w;
37192                 s[i].style.left = (pos-this.splitOffset) + "px";
37193             }
37194         }
37195     },
37196
37197     handleHiddenChange : function(colModel, colIndex, hidden){
37198         if(hidden){
37199             this.hideColumn(colIndex);
37200         }else{
37201             this.unhideColumn(colIndex);
37202         }
37203     },
37204
37205     hideColumn : function(colIndex){
37206         var cid = this.getColumnId(colIndex);
37207         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37208         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37209         if(Roo.isSafari){
37210             this.updateHeaders();
37211         }
37212         this.updateSplitters();
37213         this.layout();
37214     },
37215
37216     unhideColumn : function(colIndex){
37217         var cid = this.getColumnId(colIndex);
37218         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37219         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37220
37221         if(Roo.isSafari){
37222             this.updateHeaders();
37223         }
37224         this.updateSplitters();
37225         this.layout();
37226     },
37227
37228     insertRows : function(dm, firstRow, lastRow, isUpdate){
37229         if(firstRow == 0 && lastRow == dm.getCount()-1){
37230             this.refresh();
37231         }else{
37232             if(!isUpdate){
37233                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37234             }
37235             var s = this.getScrollState();
37236             var markup = this.renderRows(firstRow, lastRow);
37237             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37238             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37239             this.restoreScroll(s);
37240             if(!isUpdate){
37241                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37242                 this.syncRowHeights(firstRow, lastRow);
37243                 this.stripeRows(firstRow);
37244                 this.layout();
37245             }
37246         }
37247     },
37248
37249     bufferRows : function(markup, target, index){
37250         var before = null, trows = target.rows, tbody = target.tBodies[0];
37251         if(index < trows.length){
37252             before = trows[index];
37253         }
37254         var b = document.createElement("div");
37255         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37256         var rows = b.firstChild.rows;
37257         for(var i = 0, len = rows.length; i < len; i++){
37258             if(before){
37259                 tbody.insertBefore(rows[0], before);
37260             }else{
37261                 tbody.appendChild(rows[0]);
37262             }
37263         }
37264         b.innerHTML = "";
37265         b = null;
37266     },
37267
37268     deleteRows : function(dm, firstRow, lastRow){
37269         if(dm.getRowCount()<1){
37270             this.fireEvent("beforerefresh", this);
37271             this.mainBody.update("");
37272             this.lockedBody.update("");
37273             this.fireEvent("refresh", this);
37274         }else{
37275             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37276             var bt = this.getBodyTable();
37277             var tbody = bt.firstChild;
37278             var rows = bt.rows;
37279             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37280                 tbody.removeChild(rows[firstRow]);
37281             }
37282             this.stripeRows(firstRow);
37283             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37284         }
37285     },
37286
37287     updateRows : function(dataSource, firstRow, lastRow){
37288         var s = this.getScrollState();
37289         this.refresh();
37290         this.restoreScroll(s);
37291     },
37292
37293     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37294         if(!noRefresh){
37295            this.refresh();
37296         }
37297         this.updateHeaderSortState();
37298     },
37299
37300     getScrollState : function(){
37301         
37302         var sb = this.scroller.dom;
37303         return {left: sb.scrollLeft, top: sb.scrollTop};
37304     },
37305
37306     stripeRows : function(startRow){
37307         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37308             return;
37309         }
37310         startRow = startRow || 0;
37311         var rows = this.getBodyTable().rows;
37312         var lrows = this.getLockedTable().rows;
37313         var cls = ' x-grid-row-alt ';
37314         for(var i = startRow, len = rows.length; i < len; i++){
37315             var row = rows[i], lrow = lrows[i];
37316             var isAlt = ((i+1) % 2 == 0);
37317             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37318             if(isAlt == hasAlt){
37319                 continue;
37320             }
37321             if(isAlt){
37322                 row.className += " x-grid-row-alt";
37323             }else{
37324                 row.className = row.className.replace("x-grid-row-alt", "");
37325             }
37326             if(lrow){
37327                 lrow.className = row.className;
37328             }
37329         }
37330     },
37331
37332     restoreScroll : function(state){
37333         //Roo.log('GridView.restoreScroll');
37334         var sb = this.scroller.dom;
37335         sb.scrollLeft = state.left;
37336         sb.scrollTop = state.top;
37337         this.syncScroll();
37338     },
37339
37340     syncScroll : function(){
37341         //Roo.log('GridView.syncScroll');
37342         var sb = this.scroller.dom;
37343         var sh = this.mainHd.dom;
37344         var bs = this.mainBody.dom;
37345         var lv = this.lockedBody.dom;
37346         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37347         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37348     },
37349
37350     handleScroll : function(e){
37351         this.syncScroll();
37352         var sb = this.scroller.dom;
37353         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37354         e.stopEvent();
37355     },
37356
37357     handleWheel : function(e){
37358         var d = e.getWheelDelta();
37359         this.scroller.dom.scrollTop -= d*22;
37360         // set this here to prevent jumpy scrolling on large tables
37361         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37362         e.stopEvent();
37363     },
37364
37365     renderRows : function(startRow, endRow){
37366         // pull in all the crap needed to render rows
37367         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37368         var colCount = cm.getColumnCount();
37369
37370         if(ds.getCount() < 1){
37371             return ["", ""];
37372         }
37373
37374         // build a map for all the columns
37375         var cs = [];
37376         for(var i = 0; i < colCount; i++){
37377             var name = cm.getDataIndex(i);
37378             cs[i] = {
37379                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37380                 renderer : cm.getRenderer(i),
37381                 id : cm.getColumnId(i),
37382                 locked : cm.isLocked(i)
37383             };
37384         }
37385
37386         startRow = startRow || 0;
37387         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37388
37389         // records to render
37390         var rs = ds.getRange(startRow, endRow);
37391
37392         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37393     },
37394
37395     // As much as I hate to duplicate code, this was branched because FireFox really hates
37396     // [].join("") on strings. The performance difference was substantial enough to
37397     // branch this function
37398     doRender : Roo.isGecko ?
37399             function(cs, rs, ds, startRow, colCount, stripe){
37400                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37401                 // buffers
37402                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37403                 
37404                 var hasListener = this.grid.hasListener('rowclass');
37405                 var rowcfg = {};
37406                 for(var j = 0, len = rs.length; j < len; j++){
37407                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37408                     for(var i = 0; i < colCount; i++){
37409                         c = cs[i];
37410                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37411                         p.id = c.id;
37412                         p.css = p.attr = "";
37413                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37414                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37415                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37416                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37417                         }
37418                         var markup = ct.apply(p);
37419                         if(!c.locked){
37420                             cb+= markup;
37421                         }else{
37422                             lcb+= markup;
37423                         }
37424                     }
37425                     var alt = [];
37426                     if(stripe && ((rowIndex+1) % 2 == 0)){
37427                         alt.push("x-grid-row-alt")
37428                     }
37429                     if(r.dirty){
37430                         alt.push(  " x-grid-dirty-row");
37431                     }
37432                     rp.cells = lcb;
37433                     if(this.getRowClass){
37434                         alt.push(this.getRowClass(r, rowIndex));
37435                     }
37436                     if (hasListener) {
37437                         rowcfg = {
37438                              
37439                             record: r,
37440                             rowIndex : rowIndex,
37441                             rowClass : ''
37442                         }
37443                         this.grid.fireEvent('rowclass', this, rowcfg);
37444                         alt.push(rowcfg.rowClass);
37445                     }
37446                     rp.alt = alt.join(" ");
37447                     lbuf+= rt.apply(rp);
37448                     rp.cells = cb;
37449                     buf+=  rt.apply(rp);
37450                 }
37451                 return [lbuf, buf];
37452             } :
37453             function(cs, rs, ds, startRow, colCount, stripe){
37454                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37455                 // buffers
37456                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37457                 var hasListener = this.grid.hasListener('rowclass');
37458  
37459                 var rowcfg = {};
37460                 for(var j = 0, len = rs.length; j < len; j++){
37461                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37462                     for(var i = 0; i < colCount; i++){
37463                         c = cs[i];
37464                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37465                         p.id = c.id;
37466                         p.css = p.attr = "";
37467                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37468                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37469                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37470                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37471                         }
37472                         
37473                         var markup = ct.apply(p);
37474                         if(!c.locked){
37475                             cb[cb.length] = markup;
37476                         }else{
37477                             lcb[lcb.length] = markup;
37478                         }
37479                     }
37480                     var alt = [];
37481                     if(stripe && ((rowIndex+1) % 2 == 0)){
37482                         alt.push( "x-grid-row-alt");
37483                     }
37484                     if(r.dirty){
37485                         alt.push(" x-grid-dirty-row");
37486                     }
37487                     rp.cells = lcb;
37488                     if(this.getRowClass){
37489                         alt.push( this.getRowClass(r, rowIndex));
37490                     }
37491                     if (hasListener) {
37492                         rowcfg = {
37493                              
37494                             record: r,
37495                             rowIndex : rowIndex,
37496                             rowClass : ''
37497                         }
37498                         this.grid.fireEvent('rowclass', this, rowcfg);
37499                         alt.push(rowcfg.rowClass);
37500                     }
37501                     rp.alt = alt.join(" ");
37502                     rp.cells = lcb.join("");
37503                     lbuf[lbuf.length] = rt.apply(rp);
37504                     rp.cells = cb.join("");
37505                     buf[buf.length] =  rt.apply(rp);
37506                 }
37507                 return [lbuf.join(""), buf.join("")];
37508             },
37509
37510     renderBody : function(){
37511         var markup = this.renderRows();
37512         var bt = this.templates.body;
37513         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37514     },
37515
37516     /**
37517      * Refreshes the grid
37518      * @param {Boolean} headersToo
37519      */
37520     refresh : function(headersToo){
37521         this.fireEvent("beforerefresh", this);
37522         this.grid.stopEditing();
37523         var result = this.renderBody();
37524         this.lockedBody.update(result[0]);
37525         this.mainBody.update(result[1]);
37526         if(headersToo === true){
37527             this.updateHeaders();
37528             this.updateColumns();
37529             this.updateSplitters();
37530             this.updateHeaderSortState();
37531         }
37532         this.syncRowHeights();
37533         this.layout();
37534         this.fireEvent("refresh", this);
37535     },
37536
37537     handleColumnMove : function(cm, oldIndex, newIndex){
37538         this.indexMap = null;
37539         var s = this.getScrollState();
37540         this.refresh(true);
37541         this.restoreScroll(s);
37542         this.afterMove(newIndex);
37543     },
37544
37545     afterMove : function(colIndex){
37546         if(this.enableMoveAnim && Roo.enableFx){
37547             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37548         }
37549         // if multisort - fix sortOrder, and reload..
37550         if (this.grid.dataSource.multiSort) {
37551             // the we can call sort again..
37552             var dm = this.grid.dataSource;
37553             var cm = this.grid.colModel;
37554             var so = [];
37555             for(var i = 0; i < cm.config.length; i++ ) {
37556                 
37557                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37558                     continue; // dont' bother, it's not in sort list or being set.
37559                 }
37560                 
37561                 so.push(cm.config[i].dataIndex);
37562             };
37563             dm.sortOrder = so;
37564             dm.load(dm.lastOptions);
37565             
37566             
37567         }
37568         
37569     },
37570
37571     updateCell : function(dm, rowIndex, dataIndex){
37572         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37573         if(typeof colIndex == "undefined"){ // not present in grid
37574             return;
37575         }
37576         var cm = this.grid.colModel;
37577         var cell = this.getCell(rowIndex, colIndex);
37578         var cellText = this.getCellText(rowIndex, colIndex);
37579
37580         var p = {
37581             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37582             id : cm.getColumnId(colIndex),
37583             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37584         };
37585         var renderer = cm.getRenderer(colIndex);
37586         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37587         if(typeof val == "undefined" || val === "") val = "&#160;";
37588         cellText.innerHTML = val;
37589         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37590         this.syncRowHeights(rowIndex, rowIndex);
37591     },
37592
37593     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37594         var maxWidth = 0;
37595         if(this.grid.autoSizeHeaders){
37596             var h = this.getHeaderCellMeasure(colIndex);
37597             maxWidth = Math.max(maxWidth, h.scrollWidth);
37598         }
37599         var tb, index;
37600         if(this.cm.isLocked(colIndex)){
37601             tb = this.getLockedTable();
37602             index = colIndex;
37603         }else{
37604             tb = this.getBodyTable();
37605             index = colIndex - this.cm.getLockedCount();
37606         }
37607         if(tb && tb.rows){
37608             var rows = tb.rows;
37609             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37610             for(var i = 0; i < stopIndex; i++){
37611                 var cell = rows[i].childNodes[index].firstChild;
37612                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37613             }
37614         }
37615         return maxWidth + /*margin for error in IE*/ 5;
37616     },
37617     /**
37618      * Autofit a column to its content.
37619      * @param {Number} colIndex
37620      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37621      */
37622      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37623          if(this.cm.isHidden(colIndex)){
37624              return; // can't calc a hidden column
37625          }
37626         if(forceMinSize){
37627             var cid = this.cm.getColumnId(colIndex);
37628             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37629            if(this.grid.autoSizeHeaders){
37630                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37631            }
37632         }
37633         var newWidth = this.calcColumnWidth(colIndex);
37634         this.cm.setColumnWidth(colIndex,
37635             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37636         if(!suppressEvent){
37637             this.grid.fireEvent("columnresize", colIndex, newWidth);
37638         }
37639     },
37640
37641     /**
37642      * Autofits all columns to their content and then expands to fit any extra space in the grid
37643      */
37644      autoSizeColumns : function(){
37645         var cm = this.grid.colModel;
37646         var colCount = cm.getColumnCount();
37647         for(var i = 0; i < colCount; i++){
37648             this.autoSizeColumn(i, true, true);
37649         }
37650         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37651             this.fitColumns();
37652         }else{
37653             this.updateColumns();
37654             this.layout();
37655         }
37656     },
37657
37658     /**
37659      * Autofits all columns to the grid's width proportionate with their current size
37660      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37661      */
37662     fitColumns : function(reserveScrollSpace){
37663         var cm = this.grid.colModel;
37664         var colCount = cm.getColumnCount();
37665         var cols = [];
37666         var width = 0;
37667         var i, w;
37668         for (i = 0; i < colCount; i++){
37669             if(!cm.isHidden(i) && !cm.isFixed(i)){
37670                 w = cm.getColumnWidth(i);
37671                 cols.push(i);
37672                 cols.push(w);
37673                 width += w;
37674             }
37675         }
37676         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37677         if(reserveScrollSpace){
37678             avail -= 17;
37679         }
37680         var frac = (avail - cm.getTotalWidth())/width;
37681         while (cols.length){
37682             w = cols.pop();
37683             i = cols.pop();
37684             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37685         }
37686         this.updateColumns();
37687         this.layout();
37688     },
37689
37690     onRowSelect : function(rowIndex){
37691         var row = this.getRowComposite(rowIndex);
37692         row.addClass("x-grid-row-selected");
37693     },
37694
37695     onRowDeselect : function(rowIndex){
37696         var row = this.getRowComposite(rowIndex);
37697         row.removeClass("x-grid-row-selected");
37698     },
37699
37700     onCellSelect : function(row, col){
37701         var cell = this.getCell(row, col);
37702         if(cell){
37703             Roo.fly(cell).addClass("x-grid-cell-selected");
37704         }
37705     },
37706
37707     onCellDeselect : function(row, col){
37708         var cell = this.getCell(row, col);
37709         if(cell){
37710             Roo.fly(cell).removeClass("x-grid-cell-selected");
37711         }
37712     },
37713
37714     updateHeaderSortState : function(){
37715         
37716         // sort state can be single { field: xxx, direction : yyy}
37717         // or   { xxx=>ASC , yyy : DESC ..... }
37718         
37719         var mstate = {};
37720         if (!this.ds.multiSort) { 
37721             var state = this.ds.getSortState();
37722             if(!state){
37723                 return;
37724             }
37725             mstate[state.field] = state.direction;
37726             // FIXME... - this is not used here.. but might be elsewhere..
37727             this.sortState = state;
37728             
37729         } else {
37730             mstate = this.ds.sortToggle;
37731         }
37732         //remove existing sort classes..
37733         
37734         var sc = this.sortClasses;
37735         var hds = this.el.select(this.headerSelector).removeClass(sc);
37736         
37737         for(var f in mstate) {
37738         
37739             var sortColumn = this.cm.findColumnIndex(f);
37740             
37741             if(sortColumn != -1){
37742                 var sortDir = mstate[f];        
37743                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37744             }
37745         }
37746         
37747          
37748         
37749     },
37750
37751
37752     handleHeaderClick : function(g, index,e){
37753         
37754         Roo.log("header click");
37755         
37756         if (Roo.isTouch) {
37757             // touch events on header are handled by context
37758             this.handleHdCtx(g,index,e);
37759             return;
37760         }
37761         
37762         
37763         if(this.headersDisabled){
37764             return;
37765         }
37766         var dm = g.dataSource, cm = g.colModel;
37767         if(!cm.isSortable(index)){
37768             return;
37769         }
37770         g.stopEditing();
37771         
37772         if (dm.multiSort) {
37773             // update the sortOrder
37774             var so = [];
37775             for(var i = 0; i < cm.config.length; i++ ) {
37776                 
37777                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37778                     continue; // dont' bother, it's not in sort list or being set.
37779                 }
37780                 
37781                 so.push(cm.config[i].dataIndex);
37782             };
37783             dm.sortOrder = so;
37784         }
37785         
37786         
37787         dm.sort(cm.getDataIndex(index));
37788     },
37789
37790
37791     destroy : function(){
37792         if(this.colMenu){
37793             this.colMenu.removeAll();
37794             Roo.menu.MenuMgr.unregister(this.colMenu);
37795             this.colMenu.getEl().remove();
37796             delete this.colMenu;
37797         }
37798         if(this.hmenu){
37799             this.hmenu.removeAll();
37800             Roo.menu.MenuMgr.unregister(this.hmenu);
37801             this.hmenu.getEl().remove();
37802             delete this.hmenu;
37803         }
37804         if(this.grid.enableColumnMove){
37805             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37806             if(dds){
37807                 for(var dd in dds){
37808                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37809                         var elid = dds[dd].dragElId;
37810                         dds[dd].unreg();
37811                         Roo.get(elid).remove();
37812                     } else if(dds[dd].config.isTarget){
37813                         dds[dd].proxyTop.remove();
37814                         dds[dd].proxyBottom.remove();
37815                         dds[dd].unreg();
37816                     }
37817                     if(Roo.dd.DDM.locationCache[dd]){
37818                         delete Roo.dd.DDM.locationCache[dd];
37819                     }
37820                 }
37821                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37822             }
37823         }
37824         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37825         this.bind(null, null);
37826         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37827     },
37828
37829     handleLockChange : function(){
37830         this.refresh(true);
37831     },
37832
37833     onDenyColumnLock : function(){
37834
37835     },
37836
37837     onDenyColumnHide : function(){
37838
37839     },
37840
37841     handleHdMenuClick : function(item){
37842         var index = this.hdCtxIndex;
37843         var cm = this.cm, ds = this.ds;
37844         switch(item.id){
37845             case "asc":
37846                 ds.sort(cm.getDataIndex(index), "ASC");
37847                 break;
37848             case "desc":
37849                 ds.sort(cm.getDataIndex(index), "DESC");
37850                 break;
37851             case "lock":
37852                 var lc = cm.getLockedCount();
37853                 if(cm.getColumnCount(true) <= lc+1){
37854                     this.onDenyColumnLock();
37855                     return;
37856                 }
37857                 if(lc != index){
37858                     cm.setLocked(index, true, true);
37859                     cm.moveColumn(index, lc);
37860                     this.grid.fireEvent("columnmove", index, lc);
37861                 }else{
37862                     cm.setLocked(index, true);
37863                 }
37864             break;
37865             case "unlock":
37866                 var lc = cm.getLockedCount();
37867                 if((lc-1) != index){
37868                     cm.setLocked(index, false, true);
37869                     cm.moveColumn(index, lc-1);
37870                     this.grid.fireEvent("columnmove", index, lc-1);
37871                 }else{
37872                     cm.setLocked(index, false);
37873                 }
37874             break;
37875             case 'wider': // used to expand cols on touch..
37876             case 'narrow':
37877                 var cw = cm.getColumnWidth(index);
37878                 cw += (item.id == 'wider' ? 1 : -1) * 50;
37879                 cw = Math.max(0, cw);
37880                 cw = Math.min(cw,4000);
37881                 cm.setColumnWidth(index, cw);
37882                 break;
37883                 
37884             default:
37885                 index = cm.getIndexById(item.id.substr(4));
37886                 if(index != -1){
37887                     if(item.checked && cm.getColumnCount(true) <= 1){
37888                         this.onDenyColumnHide();
37889                         return false;
37890                     }
37891                     cm.setHidden(index, item.checked);
37892                 }
37893         }
37894         return true;
37895     },
37896
37897     beforeColMenuShow : function(){
37898         var cm = this.cm,  colCount = cm.getColumnCount();
37899         this.colMenu.removeAll();
37900         for(var i = 0; i < colCount; i++){
37901             this.colMenu.add(new Roo.menu.CheckItem({
37902                 id: "col-"+cm.getColumnId(i),
37903                 text: cm.getColumnHeader(i),
37904                 checked: !cm.isHidden(i),
37905                 hideOnClick:false
37906             }));
37907         }
37908     },
37909
37910     handleHdCtx : function(g, index, e){
37911         e.stopEvent();
37912         var hd = this.getHeaderCell(index);
37913         this.hdCtxIndex = index;
37914         var ms = this.hmenu.items, cm = this.cm;
37915         ms.get("asc").setDisabled(!cm.isSortable(index));
37916         ms.get("desc").setDisabled(!cm.isSortable(index));
37917         if(this.grid.enableColLock !== false){
37918             ms.get("lock").setDisabled(cm.isLocked(index));
37919             ms.get("unlock").setDisabled(!cm.isLocked(index));
37920         }
37921         this.hmenu.show(hd, "tl-bl");
37922     },
37923
37924     handleHdOver : function(e){
37925         var hd = this.findHeaderCell(e.getTarget());
37926         if(hd && !this.headersDisabled){
37927             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37928                this.fly(hd).addClass("x-grid-hd-over");
37929             }
37930         }
37931     },
37932
37933     handleHdOut : function(e){
37934         var hd = this.findHeaderCell(e.getTarget());
37935         if(hd){
37936             this.fly(hd).removeClass("x-grid-hd-over");
37937         }
37938     },
37939
37940     handleSplitDblClick : function(e, t){
37941         var i = this.getCellIndex(t);
37942         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37943             this.autoSizeColumn(i, true);
37944             this.layout();
37945         }
37946     },
37947
37948     render : function(){
37949
37950         var cm = this.cm;
37951         var colCount = cm.getColumnCount();
37952
37953         if(this.grid.monitorWindowResize === true){
37954             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37955         }
37956         var header = this.renderHeaders();
37957         var body = this.templates.body.apply({rows:""});
37958         var html = this.templates.master.apply({
37959             lockedBody: body,
37960             body: body,
37961             lockedHeader: header[0],
37962             header: header[1]
37963         });
37964
37965         //this.updateColumns();
37966
37967         this.grid.getGridEl().dom.innerHTML = html;
37968
37969         this.initElements();
37970         
37971         // a kludge to fix the random scolling effect in webkit
37972         this.el.on("scroll", function() {
37973             this.el.dom.scrollTop=0; // hopefully not recursive..
37974         },this);
37975
37976         this.scroller.on("scroll", this.handleScroll, this);
37977         this.lockedBody.on("mousewheel", this.handleWheel, this);
37978         this.mainBody.on("mousewheel", this.handleWheel, this);
37979
37980         this.mainHd.on("mouseover", this.handleHdOver, this);
37981         this.mainHd.on("mouseout", this.handleHdOut, this);
37982         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37983                 {delegate: "."+this.splitClass});
37984
37985         this.lockedHd.on("mouseover", this.handleHdOver, this);
37986         this.lockedHd.on("mouseout", this.handleHdOut, this);
37987         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37988                 {delegate: "."+this.splitClass});
37989
37990         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37991             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37992         }
37993
37994         this.updateSplitters();
37995
37996         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37997             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37998             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37999         }
38000
38001         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38002             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38003             this.hmenu.add(
38004                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38005                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38006             );
38007             if(this.grid.enableColLock !== false){
38008                 this.hmenu.add('-',
38009                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38010                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38011                 );
38012             }
38013             if (Roo.isTouch) {
38014                  this.hmenu.add('-',
38015                     {id:"wider", text: this.columnsWiderText},
38016                     {id:"narrow", text: this.columnsNarrowText }
38017                 );
38018                 
38019                  
38020             }
38021             
38022             if(this.grid.enableColumnHide !== false){
38023
38024                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38025                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38026                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38027
38028                 this.hmenu.add('-',
38029                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38030                 );
38031             }
38032             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38033
38034             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38035         }
38036
38037         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38038             this.dd = new Roo.grid.GridDragZone(this.grid, {
38039                 ddGroup : this.grid.ddGroup || 'GridDD'
38040             });
38041             
38042         }
38043
38044         /*
38045         for(var i = 0; i < colCount; i++){
38046             if(cm.isHidden(i)){
38047                 this.hideColumn(i);
38048             }
38049             if(cm.config[i].align){
38050                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38051                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38052             }
38053         }*/
38054         
38055         this.updateHeaderSortState();
38056
38057         this.beforeInitialResize();
38058         this.layout(true);
38059
38060         // two part rendering gives faster view to the user
38061         this.renderPhase2.defer(1, this);
38062     },
38063
38064     renderPhase2 : function(){
38065         // render the rows now
38066         this.refresh();
38067         if(this.grid.autoSizeColumns){
38068             this.autoSizeColumns();
38069         }
38070     },
38071
38072     beforeInitialResize : function(){
38073
38074     },
38075
38076     onColumnSplitterMoved : function(i, w){
38077         this.userResized = true;
38078         var cm = this.grid.colModel;
38079         cm.setColumnWidth(i, w, true);
38080         var cid = cm.getColumnId(i);
38081         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38082         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38083         this.updateSplitters();
38084         this.layout();
38085         this.grid.fireEvent("columnresize", i, w);
38086     },
38087
38088     syncRowHeights : function(startIndex, endIndex){
38089         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38090             startIndex = startIndex || 0;
38091             var mrows = this.getBodyTable().rows;
38092             var lrows = this.getLockedTable().rows;
38093             var len = mrows.length-1;
38094             endIndex = Math.min(endIndex || len, len);
38095             for(var i = startIndex; i <= endIndex; i++){
38096                 var m = mrows[i], l = lrows[i];
38097                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38098                 m.style.height = l.style.height = h + "px";
38099             }
38100         }
38101     },
38102
38103     layout : function(initialRender, is2ndPass){
38104         var g = this.grid;
38105         var auto = g.autoHeight;
38106         var scrollOffset = 16;
38107         var c = g.getGridEl(), cm = this.cm,
38108                 expandCol = g.autoExpandColumn,
38109                 gv = this;
38110         //c.beginMeasure();
38111
38112         if(!c.dom.offsetWidth){ // display:none?
38113             if(initialRender){
38114                 this.lockedWrap.show();
38115                 this.mainWrap.show();
38116             }
38117             return;
38118         }
38119
38120         var hasLock = this.cm.isLocked(0);
38121
38122         var tbh = this.headerPanel.getHeight();
38123         var bbh = this.footerPanel.getHeight();
38124
38125         if(auto){
38126             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38127             var newHeight = ch + c.getBorderWidth("tb");
38128             if(g.maxHeight){
38129                 newHeight = Math.min(g.maxHeight, newHeight);
38130             }
38131             c.setHeight(newHeight);
38132         }
38133
38134         if(g.autoWidth){
38135             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38136         }
38137
38138         var s = this.scroller;
38139
38140         var csize = c.getSize(true);
38141
38142         this.el.setSize(csize.width, csize.height);
38143
38144         this.headerPanel.setWidth(csize.width);
38145         this.footerPanel.setWidth(csize.width);
38146
38147         var hdHeight = this.mainHd.getHeight();
38148         var vw = csize.width;
38149         var vh = csize.height - (tbh + bbh);
38150
38151         s.setSize(vw, vh);
38152
38153         var bt = this.getBodyTable();
38154         var ltWidth = hasLock ?
38155                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38156
38157         var scrollHeight = bt.offsetHeight;
38158         var scrollWidth = ltWidth + bt.offsetWidth;
38159         var vscroll = false, hscroll = false;
38160
38161         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38162
38163         var lw = this.lockedWrap, mw = this.mainWrap;
38164         var lb = this.lockedBody, mb = this.mainBody;
38165
38166         setTimeout(function(){
38167             var t = s.dom.offsetTop;
38168             var w = s.dom.clientWidth,
38169                 h = s.dom.clientHeight;
38170
38171             lw.setTop(t);
38172             lw.setSize(ltWidth, h);
38173
38174             mw.setLeftTop(ltWidth, t);
38175             mw.setSize(w-ltWidth, h);
38176
38177             lb.setHeight(h-hdHeight);
38178             mb.setHeight(h-hdHeight);
38179
38180             if(is2ndPass !== true && !gv.userResized && expandCol){
38181                 // high speed resize without full column calculation
38182                 
38183                 var ci = cm.getIndexById(expandCol);
38184                 if (ci < 0) {
38185                     ci = cm.findColumnIndex(expandCol);
38186                 }
38187                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38188                 var expandId = cm.getColumnId(ci);
38189                 var  tw = cm.getTotalWidth(false);
38190                 var currentWidth = cm.getColumnWidth(ci);
38191                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38192                 if(currentWidth != cw){
38193                     cm.setColumnWidth(ci, cw, true);
38194                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38195                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38196                     gv.updateSplitters();
38197                     gv.layout(false, true);
38198                 }
38199             }
38200
38201             if(initialRender){
38202                 lw.show();
38203                 mw.show();
38204             }
38205             //c.endMeasure();
38206         }, 10);
38207     },
38208
38209     onWindowResize : function(){
38210         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38211             return;
38212         }
38213         this.layout();
38214     },
38215
38216     appendFooter : function(parentEl){
38217         return null;
38218     },
38219
38220     sortAscText : "Sort Ascending",
38221     sortDescText : "Sort Descending",
38222     lockText : "Lock Column",
38223     unlockText : "Unlock Column",
38224     columnsText : "Columns",
38225  
38226     columnsWiderText : "Wider",
38227     columnsNarrowText : "Thinner"
38228 });
38229
38230
38231 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38232     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38233     this.proxy.el.addClass('x-grid3-col-dd');
38234 };
38235
38236 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38237     handleMouseDown : function(e){
38238
38239     },
38240
38241     callHandleMouseDown : function(e){
38242         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38243     }
38244 });
38245 /*
38246  * Based on:
38247  * Ext JS Library 1.1.1
38248  * Copyright(c) 2006-2007, Ext JS, LLC.
38249  *
38250  * Originally Released Under LGPL - original licence link has changed is not relivant.
38251  *
38252  * Fork - LGPL
38253  * <script type="text/javascript">
38254  */
38255  
38256 // private
38257 // This is a support class used internally by the Grid components
38258 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38259     this.grid = grid;
38260     this.view = grid.getView();
38261     this.proxy = this.view.resizeProxy;
38262     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38263         "gridSplitters" + this.grid.getGridEl().id, {
38264         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38265     });
38266     this.setHandleElId(Roo.id(hd));
38267     this.setOuterHandleElId(Roo.id(hd2));
38268     this.scroll = false;
38269 };
38270 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38271     fly: Roo.Element.fly,
38272
38273     b4StartDrag : function(x, y){
38274         this.view.headersDisabled = true;
38275         this.proxy.setHeight(this.view.mainWrap.getHeight());
38276         var w = this.cm.getColumnWidth(this.cellIndex);
38277         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38278         this.resetConstraints();
38279         this.setXConstraint(minw, 1000);
38280         this.setYConstraint(0, 0);
38281         this.minX = x - minw;
38282         this.maxX = x + 1000;
38283         this.startPos = x;
38284         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38285     },
38286
38287
38288     handleMouseDown : function(e){
38289         ev = Roo.EventObject.setEvent(e);
38290         var t = this.fly(ev.getTarget());
38291         if(t.hasClass("x-grid-split")){
38292             this.cellIndex = this.view.getCellIndex(t.dom);
38293             this.split = t.dom;
38294             this.cm = this.grid.colModel;
38295             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38296                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38297             }
38298         }
38299     },
38300
38301     endDrag : function(e){
38302         this.view.headersDisabled = false;
38303         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38304         var diff = endX - this.startPos;
38305         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38306     },
38307
38308     autoOffset : function(){
38309         this.setDelta(0,0);
38310     }
38311 });/*
38312  * Based on:
38313  * Ext JS Library 1.1.1
38314  * Copyright(c) 2006-2007, Ext JS, LLC.
38315  *
38316  * Originally Released Under LGPL - original licence link has changed is not relivant.
38317  *
38318  * Fork - LGPL
38319  * <script type="text/javascript">
38320  */
38321  
38322 // private
38323 // This is a support class used internally by the Grid components
38324 Roo.grid.GridDragZone = function(grid, config){
38325     this.view = grid.getView();
38326     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38327     if(this.view.lockedBody){
38328         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38329         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38330     }
38331     this.scroll = false;
38332     this.grid = grid;
38333     this.ddel = document.createElement('div');
38334     this.ddel.className = 'x-grid-dd-wrap';
38335 };
38336
38337 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38338     ddGroup : "GridDD",
38339
38340     getDragData : function(e){
38341         var t = Roo.lib.Event.getTarget(e);
38342         var rowIndex = this.view.findRowIndex(t);
38343         var sm = this.grid.selModel;
38344             
38345         //Roo.log(rowIndex);
38346         
38347         if (sm.getSelectedCell) {
38348             // cell selection..
38349             if (!sm.getSelectedCell()) {
38350                 return false;
38351             }
38352             if (rowIndex != sm.getSelectedCell()[0]) {
38353                 return false;
38354             }
38355         
38356         }
38357         
38358         if(rowIndex !== false){
38359             
38360             // if editorgrid.. 
38361             
38362             
38363             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38364                
38365             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38366               //  
38367             //}
38368             if (e.hasModifier()){
38369                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38370             }
38371             
38372             Roo.log("getDragData");
38373             
38374             return {
38375                 grid: this.grid,
38376                 ddel: this.ddel,
38377                 rowIndex: rowIndex,
38378                 selections:sm.getSelections ? sm.getSelections() : (
38379                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38380                 )
38381             };
38382         }
38383         return false;
38384     },
38385
38386     onInitDrag : function(e){
38387         var data = this.dragData;
38388         this.ddel.innerHTML = this.grid.getDragDropText();
38389         this.proxy.update(this.ddel);
38390         // fire start drag?
38391     },
38392
38393     afterRepair : function(){
38394         this.dragging = false;
38395     },
38396
38397     getRepairXY : function(e, data){
38398         return false;
38399     },
38400
38401     onEndDrag : function(data, e){
38402         // fire end drag?
38403     },
38404
38405     onValidDrop : function(dd, e, id){
38406         // fire drag drop?
38407         this.hideProxy();
38408     },
38409
38410     beforeInvalidDrop : function(e, id){
38411
38412     }
38413 });/*
38414  * Based on:
38415  * Ext JS Library 1.1.1
38416  * Copyright(c) 2006-2007, Ext JS, LLC.
38417  *
38418  * Originally Released Under LGPL - original licence link has changed is not relivant.
38419  *
38420  * Fork - LGPL
38421  * <script type="text/javascript">
38422  */
38423  
38424
38425 /**
38426  * @class Roo.grid.ColumnModel
38427  * @extends Roo.util.Observable
38428  * This is the default implementation of a ColumnModel used by the Grid. It defines
38429  * the columns in the grid.
38430  * <br>Usage:<br>
38431  <pre><code>
38432  var colModel = new Roo.grid.ColumnModel([
38433         {header: "Ticker", width: 60, sortable: true, locked: true},
38434         {header: "Company Name", width: 150, sortable: true},
38435         {header: "Market Cap.", width: 100, sortable: true},
38436         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38437         {header: "Employees", width: 100, sortable: true, resizable: false}
38438  ]);
38439  </code></pre>
38440  * <p>
38441  
38442  * The config options listed for this class are options which may appear in each
38443  * individual column definition.
38444  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38445  * @constructor
38446  * @param {Object} config An Array of column config objects. See this class's
38447  * config objects for details.
38448 */
38449 Roo.grid.ColumnModel = function(config){
38450         /**
38451      * The config passed into the constructor
38452      */
38453     this.config = config;
38454     this.lookup = {};
38455
38456     // if no id, create one
38457     // if the column does not have a dataIndex mapping,
38458     // map it to the order it is in the config
38459     for(var i = 0, len = config.length; i < len; i++){
38460         var c = config[i];
38461         if(typeof c.dataIndex == "undefined"){
38462             c.dataIndex = i;
38463         }
38464         if(typeof c.renderer == "string"){
38465             c.renderer = Roo.util.Format[c.renderer];
38466         }
38467         if(typeof c.id == "undefined"){
38468             c.id = Roo.id();
38469         }
38470         if(c.editor && c.editor.xtype){
38471             c.editor  = Roo.factory(c.editor, Roo.grid);
38472         }
38473         if(c.editor && c.editor.isFormField){
38474             c.editor = new Roo.grid.GridEditor(c.editor);
38475         }
38476         this.lookup[c.id] = c;
38477     }
38478
38479     /**
38480      * The width of columns which have no width specified (defaults to 100)
38481      * @type Number
38482      */
38483     this.defaultWidth = 100;
38484
38485     /**
38486      * Default sortable of columns which have no sortable specified (defaults to false)
38487      * @type Boolean
38488      */
38489     this.defaultSortable = false;
38490
38491     this.addEvents({
38492         /**
38493              * @event widthchange
38494              * Fires when the width of a column changes.
38495              * @param {ColumnModel} this
38496              * @param {Number} columnIndex The column index
38497              * @param {Number} newWidth The new width
38498              */
38499             "widthchange": true,
38500         /**
38501              * @event headerchange
38502              * Fires when the text of a header changes.
38503              * @param {ColumnModel} this
38504              * @param {Number} columnIndex The column index
38505              * @param {Number} newText The new header text
38506              */
38507             "headerchange": true,
38508         /**
38509              * @event hiddenchange
38510              * Fires when a column is hidden or "unhidden".
38511              * @param {ColumnModel} this
38512              * @param {Number} columnIndex The column index
38513              * @param {Boolean} hidden true if hidden, false otherwise
38514              */
38515             "hiddenchange": true,
38516             /**
38517          * @event columnmoved
38518          * Fires when a column is moved.
38519          * @param {ColumnModel} this
38520          * @param {Number} oldIndex
38521          * @param {Number} newIndex
38522          */
38523         "columnmoved" : true,
38524         /**
38525          * @event columlockchange
38526          * Fires when a column's locked state is changed
38527          * @param {ColumnModel} this
38528          * @param {Number} colIndex
38529          * @param {Boolean} locked true if locked
38530          */
38531         "columnlockchange" : true
38532     });
38533     Roo.grid.ColumnModel.superclass.constructor.call(this);
38534 };
38535 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38536     /**
38537      * @cfg {String} header The header text to display in the Grid view.
38538      */
38539     /**
38540      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38541      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38542      * specified, the column's index is used as an index into the Record's data Array.
38543      */
38544     /**
38545      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38546      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38547      */
38548     /**
38549      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38550      * Defaults to the value of the {@link #defaultSortable} property.
38551      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38552      */
38553     /**
38554      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38555      */
38556     /**
38557      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38558      */
38559     /**
38560      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38561      */
38562     /**
38563      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38564      */
38565     /**
38566      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38567      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38568      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38569      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38570      */
38571        /**
38572      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38573      */
38574     /**
38575      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38576      */
38577
38578     /**
38579      * Returns the id of the column at the specified index.
38580      * @param {Number} index The column index
38581      * @return {String} the id
38582      */
38583     getColumnId : function(index){
38584         return this.config[index].id;
38585     },
38586
38587     /**
38588      * Returns the column for a specified id.
38589      * @param {String} id The column id
38590      * @return {Object} the column
38591      */
38592     getColumnById : function(id){
38593         return this.lookup[id];
38594     },
38595
38596     
38597     /**
38598      * Returns the column for a specified dataIndex.
38599      * @param {String} dataIndex The column dataIndex
38600      * @return {Object|Boolean} the column or false if not found
38601      */
38602     getColumnByDataIndex: function(dataIndex){
38603         var index = this.findColumnIndex(dataIndex);
38604         return index > -1 ? this.config[index] : false;
38605     },
38606     
38607     /**
38608      * Returns the index for a specified column id.
38609      * @param {String} id The column id
38610      * @return {Number} the index, or -1 if not found
38611      */
38612     getIndexById : function(id){
38613         for(var i = 0, len = this.config.length; i < len; i++){
38614             if(this.config[i].id == id){
38615                 return i;
38616             }
38617         }
38618         return -1;
38619     },
38620     
38621     /**
38622      * Returns the index for a specified column dataIndex.
38623      * @param {String} dataIndex The column dataIndex
38624      * @return {Number} the index, or -1 if not found
38625      */
38626     
38627     findColumnIndex : function(dataIndex){
38628         for(var i = 0, len = this.config.length; i < len; i++){
38629             if(this.config[i].dataIndex == dataIndex){
38630                 return i;
38631             }
38632         }
38633         return -1;
38634     },
38635     
38636     
38637     moveColumn : function(oldIndex, newIndex){
38638         var c = this.config[oldIndex];
38639         this.config.splice(oldIndex, 1);
38640         this.config.splice(newIndex, 0, c);
38641         this.dataMap = null;
38642         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38643     },
38644
38645     isLocked : function(colIndex){
38646         return this.config[colIndex].locked === true;
38647     },
38648
38649     setLocked : function(colIndex, value, suppressEvent){
38650         if(this.isLocked(colIndex) == value){
38651             return;
38652         }
38653         this.config[colIndex].locked = value;
38654         if(!suppressEvent){
38655             this.fireEvent("columnlockchange", this, colIndex, value);
38656         }
38657     },
38658
38659     getTotalLockedWidth : function(){
38660         var totalWidth = 0;
38661         for(var i = 0; i < this.config.length; i++){
38662             if(this.isLocked(i) && !this.isHidden(i)){
38663                 this.totalWidth += this.getColumnWidth(i);
38664             }
38665         }
38666         return totalWidth;
38667     },
38668
38669     getLockedCount : function(){
38670         for(var i = 0, len = this.config.length; i < len; i++){
38671             if(!this.isLocked(i)){
38672                 return i;
38673             }
38674         }
38675     },
38676
38677     /**
38678      * Returns the number of columns.
38679      * @return {Number}
38680      */
38681     getColumnCount : function(visibleOnly){
38682         if(visibleOnly === true){
38683             var c = 0;
38684             for(var i = 0, len = this.config.length; i < len; i++){
38685                 if(!this.isHidden(i)){
38686                     c++;
38687                 }
38688             }
38689             return c;
38690         }
38691         return this.config.length;
38692     },
38693
38694     /**
38695      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38696      * @param {Function} fn
38697      * @param {Object} scope (optional)
38698      * @return {Array} result
38699      */
38700     getColumnsBy : function(fn, scope){
38701         var r = [];
38702         for(var i = 0, len = this.config.length; i < len; i++){
38703             var c = this.config[i];
38704             if(fn.call(scope||this, c, i) === true){
38705                 r[r.length] = c;
38706             }
38707         }
38708         return r;
38709     },
38710
38711     /**
38712      * Returns true if the specified column is sortable.
38713      * @param {Number} col The column index
38714      * @return {Boolean}
38715      */
38716     isSortable : function(col){
38717         if(typeof this.config[col].sortable == "undefined"){
38718             return this.defaultSortable;
38719         }
38720         return this.config[col].sortable;
38721     },
38722
38723     /**
38724      * Returns the rendering (formatting) function defined for the column.
38725      * @param {Number} col The column index.
38726      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38727      */
38728     getRenderer : function(col){
38729         if(!this.config[col].renderer){
38730             return Roo.grid.ColumnModel.defaultRenderer;
38731         }
38732         return this.config[col].renderer;
38733     },
38734
38735     /**
38736      * Sets the rendering (formatting) function for a column.
38737      * @param {Number} col The column index
38738      * @param {Function} fn The function to use to process the cell's raw data
38739      * to return HTML markup for the grid view. The render function is called with
38740      * the following parameters:<ul>
38741      * <li>Data value.</li>
38742      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38743      * <li>css A CSS style string to apply to the table cell.</li>
38744      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38745      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38746      * <li>Row index</li>
38747      * <li>Column index</li>
38748      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38749      */
38750     setRenderer : function(col, fn){
38751         this.config[col].renderer = fn;
38752     },
38753
38754     /**
38755      * Returns the width for the specified column.
38756      * @param {Number} col The column index
38757      * @return {Number}
38758      */
38759     getColumnWidth : function(col){
38760         return this.config[col].width * 1 || this.defaultWidth;
38761     },
38762
38763     /**
38764      * Sets the width for a column.
38765      * @param {Number} col The column index
38766      * @param {Number} width The new width
38767      */
38768     setColumnWidth : function(col, width, suppressEvent){
38769         this.config[col].width = width;
38770         this.totalWidth = null;
38771         if(!suppressEvent){
38772              this.fireEvent("widthchange", this, col, width);
38773         }
38774     },
38775
38776     /**
38777      * Returns the total width of all columns.
38778      * @param {Boolean} includeHidden True to include hidden column widths
38779      * @return {Number}
38780      */
38781     getTotalWidth : function(includeHidden){
38782         if(!this.totalWidth){
38783             this.totalWidth = 0;
38784             for(var i = 0, len = this.config.length; i < len; i++){
38785                 if(includeHidden || !this.isHidden(i)){
38786                     this.totalWidth += this.getColumnWidth(i);
38787                 }
38788             }
38789         }
38790         return this.totalWidth;
38791     },
38792
38793     /**
38794      * Returns the header for the specified column.
38795      * @param {Number} col The column index
38796      * @return {String}
38797      */
38798     getColumnHeader : function(col){
38799         return this.config[col].header;
38800     },
38801
38802     /**
38803      * Sets the header for a column.
38804      * @param {Number} col The column index
38805      * @param {String} header The new header
38806      */
38807     setColumnHeader : function(col, header){
38808         this.config[col].header = header;
38809         this.fireEvent("headerchange", this, col, header);
38810     },
38811
38812     /**
38813      * Returns the tooltip for the specified column.
38814      * @param {Number} col The column index
38815      * @return {String}
38816      */
38817     getColumnTooltip : function(col){
38818             return this.config[col].tooltip;
38819     },
38820     /**
38821      * Sets the tooltip for a column.
38822      * @param {Number} col The column index
38823      * @param {String} tooltip The new tooltip
38824      */
38825     setColumnTooltip : function(col, tooltip){
38826             this.config[col].tooltip = tooltip;
38827     },
38828
38829     /**
38830      * Returns the dataIndex for the specified column.
38831      * @param {Number} col The column index
38832      * @return {Number}
38833      */
38834     getDataIndex : function(col){
38835         return this.config[col].dataIndex;
38836     },
38837
38838     /**
38839      * Sets the dataIndex for a column.
38840      * @param {Number} col The column index
38841      * @param {Number} dataIndex The new dataIndex
38842      */
38843     setDataIndex : function(col, dataIndex){
38844         this.config[col].dataIndex = dataIndex;
38845     },
38846
38847     
38848     
38849     /**
38850      * Returns true if the cell is editable.
38851      * @param {Number} colIndex The column index
38852      * @param {Number} rowIndex The row index
38853      * @return {Boolean}
38854      */
38855     isCellEditable : function(colIndex, rowIndex){
38856         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38857     },
38858
38859     /**
38860      * Returns the editor defined for the cell/column.
38861      * return false or null to disable editing.
38862      * @param {Number} colIndex The column index
38863      * @param {Number} rowIndex The row index
38864      * @return {Object}
38865      */
38866     getCellEditor : function(colIndex, rowIndex){
38867         return this.config[colIndex].editor;
38868     },
38869
38870     /**
38871      * Sets if a column is editable.
38872      * @param {Number} col The column index
38873      * @param {Boolean} editable True if the column is editable
38874      */
38875     setEditable : function(col, editable){
38876         this.config[col].editable = editable;
38877     },
38878
38879
38880     /**
38881      * Returns true if the column is hidden.
38882      * @param {Number} colIndex The column index
38883      * @return {Boolean}
38884      */
38885     isHidden : function(colIndex){
38886         return this.config[colIndex].hidden;
38887     },
38888
38889
38890     /**
38891      * Returns true if the column width cannot be changed
38892      */
38893     isFixed : function(colIndex){
38894         return this.config[colIndex].fixed;
38895     },
38896
38897     /**
38898      * Returns true if the column can be resized
38899      * @return {Boolean}
38900      */
38901     isResizable : function(colIndex){
38902         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38903     },
38904     /**
38905      * Sets if a column is hidden.
38906      * @param {Number} colIndex The column index
38907      * @param {Boolean} hidden True if the column is hidden
38908      */
38909     setHidden : function(colIndex, hidden){
38910         this.config[colIndex].hidden = hidden;
38911         this.totalWidth = null;
38912         this.fireEvent("hiddenchange", this, colIndex, hidden);
38913     },
38914
38915     /**
38916      * Sets the editor for a column.
38917      * @param {Number} col The column index
38918      * @param {Object} editor The editor object
38919      */
38920     setEditor : function(col, editor){
38921         this.config[col].editor = editor;
38922     }
38923 });
38924
38925 Roo.grid.ColumnModel.defaultRenderer = function(value){
38926         if(typeof value == "string" && value.length < 1){
38927             return "&#160;";
38928         }
38929         return value;
38930 };
38931
38932 // Alias for backwards compatibility
38933 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38934 /*
38935  * Based on:
38936  * Ext JS Library 1.1.1
38937  * Copyright(c) 2006-2007, Ext JS, LLC.
38938  *
38939  * Originally Released Under LGPL - original licence link has changed is not relivant.
38940  *
38941  * Fork - LGPL
38942  * <script type="text/javascript">
38943  */
38944
38945 /**
38946  * @class Roo.grid.AbstractSelectionModel
38947  * @extends Roo.util.Observable
38948  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38949  * implemented by descendant classes.  This class should not be directly instantiated.
38950  * @constructor
38951  */
38952 Roo.grid.AbstractSelectionModel = function(){
38953     this.locked = false;
38954     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38955 };
38956
38957 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38958     /** @ignore Called by the grid automatically. Do not call directly. */
38959     init : function(grid){
38960         this.grid = grid;
38961         this.initEvents();
38962     },
38963
38964     /**
38965      * Locks the selections.
38966      */
38967     lock : function(){
38968         this.locked = true;
38969     },
38970
38971     /**
38972      * Unlocks the selections.
38973      */
38974     unlock : function(){
38975         this.locked = false;
38976     },
38977
38978     /**
38979      * Returns true if the selections are locked.
38980      * @return {Boolean}
38981      */
38982     isLocked : function(){
38983         return this.locked;
38984     }
38985 });/*
38986  * Based on:
38987  * Ext JS Library 1.1.1
38988  * Copyright(c) 2006-2007, Ext JS, LLC.
38989  *
38990  * Originally Released Under LGPL - original licence link has changed is not relivant.
38991  *
38992  * Fork - LGPL
38993  * <script type="text/javascript">
38994  */
38995 /**
38996  * @extends Roo.grid.AbstractSelectionModel
38997  * @class Roo.grid.RowSelectionModel
38998  * The default SelectionModel used by {@link Roo.grid.Grid}.
38999  * It supports multiple selections and keyboard selection/navigation. 
39000  * @constructor
39001  * @param {Object} config
39002  */
39003 Roo.grid.RowSelectionModel = function(config){
39004     Roo.apply(this, config);
39005     this.selections = new Roo.util.MixedCollection(false, function(o){
39006         return o.id;
39007     });
39008
39009     this.last = false;
39010     this.lastActive = false;
39011
39012     this.addEvents({
39013         /**
39014              * @event selectionchange
39015              * Fires when the selection changes
39016              * @param {SelectionModel} this
39017              */
39018             "selectionchange" : true,
39019         /**
39020              * @event afterselectionchange
39021              * Fires after the selection changes (eg. by key press or clicking)
39022              * @param {SelectionModel} this
39023              */
39024             "afterselectionchange" : true,
39025         /**
39026              * @event beforerowselect
39027              * Fires when a row is selected being selected, return false to cancel.
39028              * @param {SelectionModel} this
39029              * @param {Number} rowIndex The selected index
39030              * @param {Boolean} keepExisting False if other selections will be cleared
39031              */
39032             "beforerowselect" : true,
39033         /**
39034              * @event rowselect
39035              * Fires when a row is selected.
39036              * @param {SelectionModel} this
39037              * @param {Number} rowIndex The selected index
39038              * @param {Roo.data.Record} r The record
39039              */
39040             "rowselect" : true,
39041         /**
39042              * @event rowdeselect
39043              * Fires when a row is deselected.
39044              * @param {SelectionModel} this
39045              * @param {Number} rowIndex The selected index
39046              */
39047         "rowdeselect" : true
39048     });
39049     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39050     this.locked = false;
39051 };
39052
39053 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39054     /**
39055      * @cfg {Boolean} singleSelect
39056      * True to allow selection of only one row at a time (defaults to false)
39057      */
39058     singleSelect : false,
39059
39060     // private
39061     initEvents : function(){
39062
39063         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39064             this.grid.on("mousedown", this.handleMouseDown, this);
39065         }else{ // allow click to work like normal
39066             this.grid.on("rowclick", this.handleDragableRowClick, this);
39067         }
39068
39069         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39070             "up" : function(e){
39071                 if(!e.shiftKey){
39072                     this.selectPrevious(e.shiftKey);
39073                 }else if(this.last !== false && this.lastActive !== false){
39074                     var last = this.last;
39075                     this.selectRange(this.last,  this.lastActive-1);
39076                     this.grid.getView().focusRow(this.lastActive);
39077                     if(last !== false){
39078                         this.last = last;
39079                     }
39080                 }else{
39081                     this.selectFirstRow();
39082                 }
39083                 this.fireEvent("afterselectionchange", this);
39084             },
39085             "down" : function(e){
39086                 if(!e.shiftKey){
39087                     this.selectNext(e.shiftKey);
39088                 }else if(this.last !== false && this.lastActive !== false){
39089                     var last = this.last;
39090                     this.selectRange(this.last,  this.lastActive+1);
39091                     this.grid.getView().focusRow(this.lastActive);
39092                     if(last !== false){
39093                         this.last = last;
39094                     }
39095                 }else{
39096                     this.selectFirstRow();
39097                 }
39098                 this.fireEvent("afterselectionchange", this);
39099             },
39100             scope: this
39101         });
39102
39103         var view = this.grid.view;
39104         view.on("refresh", this.onRefresh, this);
39105         view.on("rowupdated", this.onRowUpdated, this);
39106         view.on("rowremoved", this.onRemove, this);
39107     },
39108
39109     // private
39110     onRefresh : function(){
39111         var ds = this.grid.dataSource, i, v = this.grid.view;
39112         var s = this.selections;
39113         s.each(function(r){
39114             if((i = ds.indexOfId(r.id)) != -1){
39115                 v.onRowSelect(i);
39116             }else{
39117                 s.remove(r);
39118             }
39119         });
39120     },
39121
39122     // private
39123     onRemove : function(v, index, r){
39124         this.selections.remove(r);
39125     },
39126
39127     // private
39128     onRowUpdated : function(v, index, r){
39129         if(this.isSelected(r)){
39130             v.onRowSelect(index);
39131         }
39132     },
39133
39134     /**
39135      * Select records.
39136      * @param {Array} records The records to select
39137      * @param {Boolean} keepExisting (optional) True to keep existing selections
39138      */
39139     selectRecords : function(records, keepExisting){
39140         if(!keepExisting){
39141             this.clearSelections();
39142         }
39143         var ds = this.grid.dataSource;
39144         for(var i = 0, len = records.length; i < len; i++){
39145             this.selectRow(ds.indexOf(records[i]), true);
39146         }
39147     },
39148
39149     /**
39150      * Gets the number of selected rows.
39151      * @return {Number}
39152      */
39153     getCount : function(){
39154         return this.selections.length;
39155     },
39156
39157     /**
39158      * Selects the first row in the grid.
39159      */
39160     selectFirstRow : function(){
39161         this.selectRow(0);
39162     },
39163
39164     /**
39165      * Select the last row.
39166      * @param {Boolean} keepExisting (optional) True to keep existing selections
39167      */
39168     selectLastRow : function(keepExisting){
39169         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39170     },
39171
39172     /**
39173      * Selects the row immediately following the last selected row.
39174      * @param {Boolean} keepExisting (optional) True to keep existing selections
39175      */
39176     selectNext : function(keepExisting){
39177         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39178             this.selectRow(this.last+1, keepExisting);
39179             this.grid.getView().focusRow(this.last);
39180         }
39181     },
39182
39183     /**
39184      * Selects the row that precedes the last selected row.
39185      * @param {Boolean} keepExisting (optional) True to keep existing selections
39186      */
39187     selectPrevious : function(keepExisting){
39188         if(this.last){
39189             this.selectRow(this.last-1, keepExisting);
39190             this.grid.getView().focusRow(this.last);
39191         }
39192     },
39193
39194     /**
39195      * Returns the selected records
39196      * @return {Array} Array of selected records
39197      */
39198     getSelections : function(){
39199         return [].concat(this.selections.items);
39200     },
39201
39202     /**
39203      * Returns the first selected record.
39204      * @return {Record}
39205      */
39206     getSelected : function(){
39207         return this.selections.itemAt(0);
39208     },
39209
39210
39211     /**
39212      * Clears all selections.
39213      */
39214     clearSelections : function(fast){
39215         if(this.locked) return;
39216         if(fast !== true){
39217             var ds = this.grid.dataSource;
39218             var s = this.selections;
39219             s.each(function(r){
39220                 this.deselectRow(ds.indexOfId(r.id));
39221             }, this);
39222             s.clear();
39223         }else{
39224             this.selections.clear();
39225         }
39226         this.last = false;
39227     },
39228
39229
39230     /**
39231      * Selects all rows.
39232      */
39233     selectAll : function(){
39234         if(this.locked) return;
39235         this.selections.clear();
39236         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39237             this.selectRow(i, true);
39238         }
39239     },
39240
39241     /**
39242      * Returns True if there is a selection.
39243      * @return {Boolean}
39244      */
39245     hasSelection : function(){
39246         return this.selections.length > 0;
39247     },
39248
39249     /**
39250      * Returns True if the specified row is selected.
39251      * @param {Number/Record} record The record or index of the record to check
39252      * @return {Boolean}
39253      */
39254     isSelected : function(index){
39255         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39256         return (r && this.selections.key(r.id) ? true : false);
39257     },
39258
39259     /**
39260      * Returns True if the specified record id is selected.
39261      * @param {String} id The id of record to check
39262      * @return {Boolean}
39263      */
39264     isIdSelected : function(id){
39265         return (this.selections.key(id) ? true : false);
39266     },
39267
39268     // private
39269     handleMouseDown : function(e, t){
39270         var view = this.grid.getView(), rowIndex;
39271         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39272             return;
39273         };
39274         if(e.shiftKey && this.last !== false){
39275             var last = this.last;
39276             this.selectRange(last, rowIndex, e.ctrlKey);
39277             this.last = last; // reset the last
39278             view.focusRow(rowIndex);
39279         }else{
39280             var isSelected = this.isSelected(rowIndex);
39281             if(e.button !== 0 && isSelected){
39282                 view.focusRow(rowIndex);
39283             }else if(e.ctrlKey && isSelected){
39284                 this.deselectRow(rowIndex);
39285             }else if(!isSelected){
39286                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39287                 view.focusRow(rowIndex);
39288             }
39289         }
39290         this.fireEvent("afterselectionchange", this);
39291     },
39292     // private
39293     handleDragableRowClick :  function(grid, rowIndex, e) 
39294     {
39295         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39296             this.selectRow(rowIndex, false);
39297             grid.view.focusRow(rowIndex);
39298              this.fireEvent("afterselectionchange", this);
39299         }
39300     },
39301     
39302     /**
39303      * Selects multiple rows.
39304      * @param {Array} rows Array of the indexes of the row to select
39305      * @param {Boolean} keepExisting (optional) True to keep existing selections
39306      */
39307     selectRows : function(rows, keepExisting){
39308         if(!keepExisting){
39309             this.clearSelections();
39310         }
39311         for(var i = 0, len = rows.length; i < len; i++){
39312             this.selectRow(rows[i], true);
39313         }
39314     },
39315
39316     /**
39317      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39318      * @param {Number} startRow The index of the first row in the range
39319      * @param {Number} endRow The index of the last row in the range
39320      * @param {Boolean} keepExisting (optional) True to retain existing selections
39321      */
39322     selectRange : function(startRow, endRow, keepExisting){
39323         if(this.locked) return;
39324         if(!keepExisting){
39325             this.clearSelections();
39326         }
39327         if(startRow <= endRow){
39328             for(var i = startRow; i <= endRow; i++){
39329                 this.selectRow(i, true);
39330             }
39331         }else{
39332             for(var i = startRow; i >= endRow; i--){
39333                 this.selectRow(i, true);
39334             }
39335         }
39336     },
39337
39338     /**
39339      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39340      * @param {Number} startRow The index of the first row in the range
39341      * @param {Number} endRow The index of the last row in the range
39342      */
39343     deselectRange : function(startRow, endRow, preventViewNotify){
39344         if(this.locked) return;
39345         for(var i = startRow; i <= endRow; i++){
39346             this.deselectRow(i, preventViewNotify);
39347         }
39348     },
39349
39350     /**
39351      * Selects a row.
39352      * @param {Number} row The index of the row to select
39353      * @param {Boolean} keepExisting (optional) True to keep existing selections
39354      */
39355     selectRow : function(index, keepExisting, preventViewNotify){
39356         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39357         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39358             if(!keepExisting || this.singleSelect){
39359                 this.clearSelections();
39360             }
39361             var r = this.grid.dataSource.getAt(index);
39362             this.selections.add(r);
39363             this.last = this.lastActive = index;
39364             if(!preventViewNotify){
39365                 this.grid.getView().onRowSelect(index);
39366             }
39367             this.fireEvent("rowselect", this, index, r);
39368             this.fireEvent("selectionchange", this);
39369         }
39370     },
39371
39372     /**
39373      * Deselects a row.
39374      * @param {Number} row The index of the row to deselect
39375      */
39376     deselectRow : function(index, preventViewNotify){
39377         if(this.locked) return;
39378         if(this.last == index){
39379             this.last = false;
39380         }
39381         if(this.lastActive == index){
39382             this.lastActive = false;
39383         }
39384         var r = this.grid.dataSource.getAt(index);
39385         this.selections.remove(r);
39386         if(!preventViewNotify){
39387             this.grid.getView().onRowDeselect(index);
39388         }
39389         this.fireEvent("rowdeselect", this, index);
39390         this.fireEvent("selectionchange", this);
39391     },
39392
39393     // private
39394     restoreLast : function(){
39395         if(this._last){
39396             this.last = this._last;
39397         }
39398     },
39399
39400     // private
39401     acceptsNav : function(row, col, cm){
39402         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39403     },
39404
39405     // private
39406     onEditorKey : function(field, e){
39407         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39408         if(k == e.TAB){
39409             e.stopEvent();
39410             ed.completeEdit();
39411             if(e.shiftKey){
39412                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39413             }else{
39414                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39415             }
39416         }else if(k == e.ENTER && !e.ctrlKey){
39417             e.stopEvent();
39418             ed.completeEdit();
39419             if(e.shiftKey){
39420                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39421             }else{
39422                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39423             }
39424         }else if(k == e.ESC){
39425             ed.cancelEdit();
39426         }
39427         if(newCell){
39428             g.startEditing(newCell[0], newCell[1]);
39429         }
39430     }
39431 });/*
39432  * Based on:
39433  * Ext JS Library 1.1.1
39434  * Copyright(c) 2006-2007, Ext JS, LLC.
39435  *
39436  * Originally Released Under LGPL - original licence link has changed is not relivant.
39437  *
39438  * Fork - LGPL
39439  * <script type="text/javascript">
39440  */
39441 /**
39442  * @class Roo.grid.CellSelectionModel
39443  * @extends Roo.grid.AbstractSelectionModel
39444  * This class provides the basic implementation for cell selection in a grid.
39445  * @constructor
39446  * @param {Object} config The object containing the configuration of this model.
39447  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39448  */
39449 Roo.grid.CellSelectionModel = function(config){
39450     Roo.apply(this, config);
39451
39452     this.selection = null;
39453
39454     this.addEvents({
39455         /**
39456              * @event beforerowselect
39457              * Fires before a cell is selected.
39458              * @param {SelectionModel} this
39459              * @param {Number} rowIndex The selected row index
39460              * @param {Number} colIndex The selected cell index
39461              */
39462             "beforecellselect" : true,
39463         /**
39464              * @event cellselect
39465              * Fires when a cell is selected.
39466              * @param {SelectionModel} this
39467              * @param {Number} rowIndex The selected row index
39468              * @param {Number} colIndex The selected cell index
39469              */
39470             "cellselect" : true,
39471         /**
39472              * @event selectionchange
39473              * Fires when the active selection changes.
39474              * @param {SelectionModel} this
39475              * @param {Object} selection null for no selection or an object (o) with two properties
39476                 <ul>
39477                 <li>o.record: the record object for the row the selection is in</li>
39478                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39479                 </ul>
39480              */
39481             "selectionchange" : true,
39482         /**
39483              * @event tabend
39484              * Fires when the tab (or enter) was pressed on the last editable cell
39485              * You can use this to trigger add new row.
39486              * @param {SelectionModel} this
39487              */
39488             "tabend" : true,
39489          /**
39490              * @event beforeeditnext
39491              * Fires before the next editable sell is made active
39492              * You can use this to skip to another cell or fire the tabend
39493              *    if you set cell to false
39494              * @param {Object} eventdata object : { cell : [ row, col ] } 
39495              */
39496             "beforeeditnext" : true
39497     });
39498     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39499 };
39500
39501 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39502     
39503     enter_is_tab: false,
39504
39505     /** @ignore */
39506     initEvents : function(){
39507         this.grid.on("mousedown", this.handleMouseDown, this);
39508         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39509         var view = this.grid.view;
39510         view.on("refresh", this.onViewChange, this);
39511         view.on("rowupdated", this.onRowUpdated, this);
39512         view.on("beforerowremoved", this.clearSelections, this);
39513         view.on("beforerowsinserted", this.clearSelections, this);
39514         if(this.grid.isEditor){
39515             this.grid.on("beforeedit", this.beforeEdit,  this);
39516         }
39517     },
39518
39519         //private
39520     beforeEdit : function(e){
39521         this.select(e.row, e.column, false, true, e.record);
39522     },
39523
39524         //private
39525     onRowUpdated : function(v, index, r){
39526         if(this.selection && this.selection.record == r){
39527             v.onCellSelect(index, this.selection.cell[1]);
39528         }
39529     },
39530
39531         //private
39532     onViewChange : function(){
39533         this.clearSelections(true);
39534     },
39535
39536         /**
39537          * Returns the currently selected cell,.
39538          * @return {Array} The selected cell (row, column) or null if none selected.
39539          */
39540     getSelectedCell : function(){
39541         return this.selection ? this.selection.cell : null;
39542     },
39543
39544     /**
39545      * Clears all selections.
39546      * @param {Boolean} true to prevent the gridview from being notified about the change.
39547      */
39548     clearSelections : function(preventNotify){
39549         var s = this.selection;
39550         if(s){
39551             if(preventNotify !== true){
39552                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39553             }
39554             this.selection = null;
39555             this.fireEvent("selectionchange", this, null);
39556         }
39557     },
39558
39559     /**
39560      * Returns true if there is a selection.
39561      * @return {Boolean}
39562      */
39563     hasSelection : function(){
39564         return this.selection ? true : false;
39565     },
39566
39567     /** @ignore */
39568     handleMouseDown : function(e, t){
39569         var v = this.grid.getView();
39570         if(this.isLocked()){
39571             return;
39572         };
39573         var row = v.findRowIndex(t);
39574         var cell = v.findCellIndex(t);
39575         if(row !== false && cell !== false){
39576             this.select(row, cell);
39577         }
39578     },
39579
39580     /**
39581      * Selects a cell.
39582      * @param {Number} rowIndex
39583      * @param {Number} collIndex
39584      */
39585     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39586         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39587             this.clearSelections();
39588             r = r || this.grid.dataSource.getAt(rowIndex);
39589             this.selection = {
39590                 record : r,
39591                 cell : [rowIndex, colIndex]
39592             };
39593             if(!preventViewNotify){
39594                 var v = this.grid.getView();
39595                 v.onCellSelect(rowIndex, colIndex);
39596                 if(preventFocus !== true){
39597                     v.focusCell(rowIndex, colIndex);
39598                 }
39599             }
39600             this.fireEvent("cellselect", this, rowIndex, colIndex);
39601             this.fireEvent("selectionchange", this, this.selection);
39602         }
39603     },
39604
39605         //private
39606     isSelectable : function(rowIndex, colIndex, cm){
39607         return !cm.isHidden(colIndex);
39608     },
39609
39610     /** @ignore */
39611     handleKeyDown : function(e){
39612         //Roo.log('Cell Sel Model handleKeyDown');
39613         if(!e.isNavKeyPress()){
39614             return;
39615         }
39616         var g = this.grid, s = this.selection;
39617         if(!s){
39618             e.stopEvent();
39619             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39620             if(cell){
39621                 this.select(cell[0], cell[1]);
39622             }
39623             return;
39624         }
39625         var sm = this;
39626         var walk = function(row, col, step){
39627             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39628         };
39629         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39630         var newCell;
39631
39632       
39633
39634         switch(k){
39635             case e.TAB:
39636                 // handled by onEditorKey
39637                 if (g.isEditor && g.editing) {
39638                     return;
39639                 }
39640                 if(e.shiftKey) {
39641                     newCell = walk(r, c-1, -1);
39642                 } else {
39643                     newCell = walk(r, c+1, 1);
39644                 }
39645                 break;
39646             
39647             case e.DOWN:
39648                newCell = walk(r+1, c, 1);
39649                 break;
39650             
39651             case e.UP:
39652                 newCell = walk(r-1, c, -1);
39653                 break;
39654             
39655             case e.RIGHT:
39656                 newCell = walk(r, c+1, 1);
39657                 break;
39658             
39659             case e.LEFT:
39660                 newCell = walk(r, c-1, -1);
39661                 break;
39662             
39663             case e.ENTER:
39664                 
39665                 if(g.isEditor && !g.editing){
39666                    g.startEditing(r, c);
39667                    e.stopEvent();
39668                    return;
39669                 }
39670                 
39671                 
39672              break;
39673         };
39674         if(newCell){
39675             this.select(newCell[0], newCell[1]);
39676             e.stopEvent();
39677             
39678         }
39679     },
39680
39681     acceptsNav : function(row, col, cm){
39682         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39683     },
39684     /**
39685      * Selects a cell.
39686      * @param {Number} field (not used) - as it's normally used as a listener
39687      * @param {Number} e - event - fake it by using
39688      *
39689      * var e = Roo.EventObjectImpl.prototype;
39690      * e.keyCode = e.TAB
39691      *
39692      * 
39693      */
39694     onEditorKey : function(field, e){
39695         
39696         var k = e.getKey(),
39697             newCell,
39698             g = this.grid,
39699             ed = g.activeEditor,
39700             forward = false;
39701         ///Roo.log('onEditorKey' + k);
39702         
39703         
39704         if (this.enter_is_tab && k == e.ENTER) {
39705             k = e.TAB;
39706         }
39707         
39708         if(k == e.TAB){
39709             if(e.shiftKey){
39710                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39711             }else{
39712                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39713                 forward = true;
39714             }
39715             
39716             e.stopEvent();
39717             
39718         } else if(k == e.ENTER &&  !e.ctrlKey){
39719             ed.completeEdit();
39720             e.stopEvent();
39721             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39722         
39723                 } else if(k == e.ESC){
39724             ed.cancelEdit();
39725         }
39726                 
39727         if (newCell) {
39728             var ecall = { cell : newCell, forward : forward };
39729             this.fireEvent('beforeeditnext', ecall );
39730             newCell = ecall.cell;
39731                         forward = ecall.forward;
39732         }
39733                 
39734         if(newCell){
39735             //Roo.log('next cell after edit');
39736             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39737         } else if (forward) {
39738             // tabbed past last
39739             this.fireEvent.defer(100, this, ['tabend',this]);
39740         }
39741     }
39742 });/*
39743  * Based on:
39744  * Ext JS Library 1.1.1
39745  * Copyright(c) 2006-2007, Ext JS, LLC.
39746  *
39747  * Originally Released Under LGPL - original licence link has changed is not relivant.
39748  *
39749  * Fork - LGPL
39750  * <script type="text/javascript">
39751  */
39752  
39753 /**
39754  * @class Roo.grid.EditorGrid
39755  * @extends Roo.grid.Grid
39756  * Class for creating and editable grid.
39757  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39758  * The container MUST have some type of size defined for the grid to fill. The container will be 
39759  * automatically set to position relative if it isn't already.
39760  * @param {Object} dataSource The data model to bind to
39761  * @param {Object} colModel The column model with info about this grid's columns
39762  */
39763 Roo.grid.EditorGrid = function(container, config){
39764     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39765     this.getGridEl().addClass("xedit-grid");
39766
39767     if(!this.selModel){
39768         this.selModel = new Roo.grid.CellSelectionModel();
39769     }
39770
39771     this.activeEditor = null;
39772
39773         this.addEvents({
39774             /**
39775              * @event beforeedit
39776              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39777              * <ul style="padding:5px;padding-left:16px;">
39778              * <li>grid - This grid</li>
39779              * <li>record - The record being edited</li>
39780              * <li>field - The field name being edited</li>
39781              * <li>value - The value for the field being edited.</li>
39782              * <li>row - The grid row index</li>
39783              * <li>column - The grid column index</li>
39784              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39785              * </ul>
39786              * @param {Object} e An edit event (see above for description)
39787              */
39788             "beforeedit" : true,
39789             /**
39790              * @event afteredit
39791              * Fires after a cell is edited. <br />
39792              * <ul style="padding:5px;padding-left:16px;">
39793              * <li>grid - This grid</li>
39794              * <li>record - The record being edited</li>
39795              * <li>field - The field name being edited</li>
39796              * <li>value - The value being set</li>
39797              * <li>originalValue - The original value for the field, before the edit.</li>
39798              * <li>row - The grid row index</li>
39799              * <li>column - The grid column index</li>
39800              * </ul>
39801              * @param {Object} e An edit event (see above for description)
39802              */
39803             "afteredit" : true,
39804             /**
39805              * @event validateedit
39806              * Fires after a cell is edited, but before the value is set in the record. 
39807          * You can use this to modify the value being set in the field, Return false
39808              * to cancel the change. The edit event object has the following properties <br />
39809              * <ul style="padding:5px;padding-left:16px;">
39810          * <li>editor - This editor</li>
39811              * <li>grid - This grid</li>
39812              * <li>record - The record being edited</li>
39813              * <li>field - The field name being edited</li>
39814              * <li>value - The value being set</li>
39815              * <li>originalValue - The original value for the field, before the edit.</li>
39816              * <li>row - The grid row index</li>
39817              * <li>column - The grid column index</li>
39818              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39819              * </ul>
39820              * @param {Object} e An edit event (see above for description)
39821              */
39822             "validateedit" : true
39823         });
39824     this.on("bodyscroll", this.stopEditing,  this);
39825     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39826 };
39827
39828 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39829     /**
39830      * @cfg {Number} clicksToEdit
39831      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39832      */
39833     clicksToEdit: 2,
39834
39835     // private
39836     isEditor : true,
39837     // private
39838     trackMouseOver: false, // causes very odd FF errors
39839
39840     onCellDblClick : function(g, row, col){
39841         this.startEditing(row, col);
39842     },
39843
39844     onEditComplete : function(ed, value, startValue){
39845         this.editing = false;
39846         this.activeEditor = null;
39847         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39848         var r = ed.record;
39849         var field = this.colModel.getDataIndex(ed.col);
39850         var e = {
39851             grid: this,
39852             record: r,
39853             field: field,
39854             originalValue: startValue,
39855             value: value,
39856             row: ed.row,
39857             column: ed.col,
39858             cancel:false,
39859             editor: ed
39860         };
39861         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39862         cell.show();
39863           
39864         if(String(value) !== String(startValue)){
39865             
39866             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39867                 r.set(field, e.value);
39868                 // if we are dealing with a combo box..
39869                 // then we also set the 'name' colum to be the displayField
39870                 if (ed.field.displayField && ed.field.name) {
39871                     r.set(ed.field.name, ed.field.el.dom.value);
39872                 }
39873                 
39874                 delete e.cancel; //?? why!!!
39875                 this.fireEvent("afteredit", e);
39876             }
39877         } else {
39878             this.fireEvent("afteredit", e); // always fire it!
39879         }
39880         this.view.focusCell(ed.row, ed.col);
39881     },
39882
39883     /**
39884      * Starts editing the specified for the specified row/column
39885      * @param {Number} rowIndex
39886      * @param {Number} colIndex
39887      */
39888     startEditing : function(row, col){
39889         this.stopEditing();
39890         if(this.colModel.isCellEditable(col, row)){
39891             this.view.ensureVisible(row, col, true);
39892           
39893             var r = this.dataSource.getAt(row);
39894             var field = this.colModel.getDataIndex(col);
39895             var cell = Roo.get(this.view.getCell(row,col));
39896             var e = {
39897                 grid: this,
39898                 record: r,
39899                 field: field,
39900                 value: r.data[field],
39901                 row: row,
39902                 column: col,
39903                 cancel:false 
39904             };
39905             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39906                 this.editing = true;
39907                 var ed = this.colModel.getCellEditor(col, row);
39908                 
39909                 if (!ed) {
39910                     return;
39911                 }
39912                 if(!ed.rendered){
39913                     ed.render(ed.parentEl || document.body);
39914                 }
39915                 ed.field.reset();
39916                
39917                 cell.hide();
39918                 
39919                 (function(){ // complex but required for focus issues in safari, ie and opera
39920                     ed.row = row;
39921                     ed.col = col;
39922                     ed.record = r;
39923                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39924                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39925                     this.activeEditor = ed;
39926                     var v = r.data[field];
39927                     ed.startEdit(this.view.getCell(row, col), v);
39928                     // combo's with 'displayField and name set
39929                     if (ed.field.displayField && ed.field.name) {
39930                         ed.field.el.dom.value = r.data[ed.field.name];
39931                     }
39932                     
39933                     
39934                 }).defer(50, this);
39935             }
39936         }
39937     },
39938         
39939     /**
39940      * Stops any active editing
39941      */
39942     stopEditing : function(){
39943         if(this.activeEditor){
39944             this.activeEditor.completeEdit();
39945         }
39946         this.activeEditor = null;
39947     },
39948         
39949          /**
39950      * Called to get grid's drag proxy text, by default returns this.ddText.
39951      * @return {String}
39952      */
39953     getDragDropText : function(){
39954         var count = this.selModel.getSelectedCell() ? 1 : 0;
39955         return String.format(this.ddText, count, count == 1 ? '' : 's');
39956     }
39957         
39958 });/*
39959  * Based on:
39960  * Ext JS Library 1.1.1
39961  * Copyright(c) 2006-2007, Ext JS, LLC.
39962  *
39963  * Originally Released Under LGPL - original licence link has changed is not relivant.
39964  *
39965  * Fork - LGPL
39966  * <script type="text/javascript">
39967  */
39968
39969 // private - not really -- you end up using it !
39970 // This is a support class used internally by the Grid components
39971
39972 /**
39973  * @class Roo.grid.GridEditor
39974  * @extends Roo.Editor
39975  * Class for creating and editable grid elements.
39976  * @param {Object} config any settings (must include field)
39977  */
39978 Roo.grid.GridEditor = function(field, config){
39979     if (!config && field.field) {
39980         config = field;
39981         field = Roo.factory(config.field, Roo.form);
39982     }
39983     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39984     field.monitorTab = false;
39985 };
39986
39987 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39988     
39989     /**
39990      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39991      */
39992     
39993     alignment: "tl-tl",
39994     autoSize: "width",
39995     hideEl : false,
39996     cls: "x-small-editor x-grid-editor",
39997     shim:false,
39998     shadow:"frame"
39999 });/*
40000  * Based on:
40001  * Ext JS Library 1.1.1
40002  * Copyright(c) 2006-2007, Ext JS, LLC.
40003  *
40004  * Originally Released Under LGPL - original licence link has changed is not relivant.
40005  *
40006  * Fork - LGPL
40007  * <script type="text/javascript">
40008  */
40009   
40010
40011   
40012 Roo.grid.PropertyRecord = Roo.data.Record.create([
40013     {name:'name',type:'string'},  'value'
40014 ]);
40015
40016
40017 Roo.grid.PropertyStore = function(grid, source){
40018     this.grid = grid;
40019     this.store = new Roo.data.Store({
40020         recordType : Roo.grid.PropertyRecord
40021     });
40022     this.store.on('update', this.onUpdate,  this);
40023     if(source){
40024         this.setSource(source);
40025     }
40026     Roo.grid.PropertyStore.superclass.constructor.call(this);
40027 };
40028
40029
40030
40031 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40032     setSource : function(o){
40033         this.source = o;
40034         this.store.removeAll();
40035         var data = [];
40036         for(var k in o){
40037             if(this.isEditableValue(o[k])){
40038                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40039             }
40040         }
40041         this.store.loadRecords({records: data}, {}, true);
40042     },
40043
40044     onUpdate : function(ds, record, type){
40045         if(type == Roo.data.Record.EDIT){
40046             var v = record.data['value'];
40047             var oldValue = record.modified['value'];
40048             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40049                 this.source[record.id] = v;
40050                 record.commit();
40051                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40052             }else{
40053                 record.reject();
40054             }
40055         }
40056     },
40057
40058     getProperty : function(row){
40059        return this.store.getAt(row);
40060     },
40061
40062     isEditableValue: function(val){
40063         if(val && val instanceof Date){
40064             return true;
40065         }else if(typeof val == 'object' || typeof val == 'function'){
40066             return false;
40067         }
40068         return true;
40069     },
40070
40071     setValue : function(prop, value){
40072         this.source[prop] = value;
40073         this.store.getById(prop).set('value', value);
40074     },
40075
40076     getSource : function(){
40077         return this.source;
40078     }
40079 });
40080
40081 Roo.grid.PropertyColumnModel = function(grid, store){
40082     this.grid = grid;
40083     var g = Roo.grid;
40084     g.PropertyColumnModel.superclass.constructor.call(this, [
40085         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40086         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40087     ]);
40088     this.store = store;
40089     this.bselect = Roo.DomHelper.append(document.body, {
40090         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40091             {tag: 'option', value: 'true', html: 'true'},
40092             {tag: 'option', value: 'false', html: 'false'}
40093         ]
40094     });
40095     Roo.id(this.bselect);
40096     var f = Roo.form;
40097     this.editors = {
40098         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40099         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40100         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40101         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40102         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40103     };
40104     this.renderCellDelegate = this.renderCell.createDelegate(this);
40105     this.renderPropDelegate = this.renderProp.createDelegate(this);
40106 };
40107
40108 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40109     
40110     
40111     nameText : 'Name',
40112     valueText : 'Value',
40113     
40114     dateFormat : 'm/j/Y',
40115     
40116     
40117     renderDate : function(dateVal){
40118         return dateVal.dateFormat(this.dateFormat);
40119     },
40120
40121     renderBool : function(bVal){
40122         return bVal ? 'true' : 'false';
40123     },
40124
40125     isCellEditable : function(colIndex, rowIndex){
40126         return colIndex == 1;
40127     },
40128
40129     getRenderer : function(col){
40130         return col == 1 ?
40131             this.renderCellDelegate : this.renderPropDelegate;
40132     },
40133
40134     renderProp : function(v){
40135         return this.getPropertyName(v);
40136     },
40137
40138     renderCell : function(val){
40139         var rv = val;
40140         if(val instanceof Date){
40141             rv = this.renderDate(val);
40142         }else if(typeof val == 'boolean'){
40143             rv = this.renderBool(val);
40144         }
40145         return Roo.util.Format.htmlEncode(rv);
40146     },
40147
40148     getPropertyName : function(name){
40149         var pn = this.grid.propertyNames;
40150         return pn && pn[name] ? pn[name] : name;
40151     },
40152
40153     getCellEditor : function(colIndex, rowIndex){
40154         var p = this.store.getProperty(rowIndex);
40155         var n = p.data['name'], val = p.data['value'];
40156         
40157         if(typeof(this.grid.customEditors[n]) == 'string'){
40158             return this.editors[this.grid.customEditors[n]];
40159         }
40160         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40161             return this.grid.customEditors[n];
40162         }
40163         if(val instanceof Date){
40164             return this.editors['date'];
40165         }else if(typeof val == 'number'){
40166             return this.editors['number'];
40167         }else if(typeof val == 'boolean'){
40168             return this.editors['boolean'];
40169         }else{
40170             return this.editors['string'];
40171         }
40172     }
40173 });
40174
40175 /**
40176  * @class Roo.grid.PropertyGrid
40177  * @extends Roo.grid.EditorGrid
40178  * This class represents the  interface of a component based property grid control.
40179  * <br><br>Usage:<pre><code>
40180  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40181       
40182  });
40183  // set any options
40184  grid.render();
40185  * </code></pre>
40186   
40187  * @constructor
40188  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40189  * The container MUST have some type of size defined for the grid to fill. The container will be
40190  * automatically set to position relative if it isn't already.
40191  * @param {Object} config A config object that sets properties on this grid.
40192  */
40193 Roo.grid.PropertyGrid = function(container, config){
40194     config = config || {};
40195     var store = new Roo.grid.PropertyStore(this);
40196     this.store = store;
40197     var cm = new Roo.grid.PropertyColumnModel(this, store);
40198     store.store.sort('name', 'ASC');
40199     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40200         ds: store.store,
40201         cm: cm,
40202         enableColLock:false,
40203         enableColumnMove:false,
40204         stripeRows:false,
40205         trackMouseOver: false,
40206         clicksToEdit:1
40207     }, config));
40208     this.getGridEl().addClass('x-props-grid');
40209     this.lastEditRow = null;
40210     this.on('columnresize', this.onColumnResize, this);
40211     this.addEvents({
40212          /**
40213              * @event beforepropertychange
40214              * Fires before a property changes (return false to stop?)
40215              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40216              * @param {String} id Record Id
40217              * @param {String} newval New Value
40218          * @param {String} oldval Old Value
40219              */
40220         "beforepropertychange": true,
40221         /**
40222              * @event propertychange
40223              * Fires after a property changes
40224              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40225              * @param {String} id Record Id
40226              * @param {String} newval New Value
40227          * @param {String} oldval Old Value
40228              */
40229         "propertychange": true
40230     });
40231     this.customEditors = this.customEditors || {};
40232 };
40233 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40234     
40235      /**
40236      * @cfg {Object} customEditors map of colnames=> custom editors.
40237      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40238      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40239      * false disables editing of the field.
40240          */
40241     
40242       /**
40243      * @cfg {Object} propertyNames map of property Names to their displayed value
40244          */
40245     
40246     render : function(){
40247         Roo.grid.PropertyGrid.superclass.render.call(this);
40248         this.autoSize.defer(100, this);
40249     },
40250
40251     autoSize : function(){
40252         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40253         if(this.view){
40254             this.view.fitColumns();
40255         }
40256     },
40257
40258     onColumnResize : function(){
40259         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40260         this.autoSize();
40261     },
40262     /**
40263      * Sets the data for the Grid
40264      * accepts a Key => Value object of all the elements avaiable.
40265      * @param {Object} data  to appear in grid.
40266      */
40267     setSource : function(source){
40268         this.store.setSource(source);
40269         //this.autoSize();
40270     },
40271     /**
40272      * Gets all the data from the grid.
40273      * @return {Object} data  data stored in grid
40274      */
40275     getSource : function(){
40276         return this.store.getSource();
40277     }
40278 });/*
40279   
40280  * Licence LGPL
40281  
40282  */
40283  
40284 /**
40285  * @class Roo.grid.Calendar
40286  * @extends Roo.util.Grid
40287  * This class extends the Grid to provide a calendar widget
40288  * <br><br>Usage:<pre><code>
40289  var grid = new Roo.grid.Calendar("my-container-id", {
40290      ds: myDataStore,
40291      cm: myColModel,
40292      selModel: mySelectionModel,
40293      autoSizeColumns: true,
40294      monitorWindowResize: false,
40295      trackMouseOver: true
40296      eventstore : real data store..
40297  });
40298  // set any options
40299  grid.render();
40300   
40301   * @constructor
40302  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40303  * The container MUST have some type of size defined for the grid to fill. The container will be
40304  * automatically set to position relative if it isn't already.
40305  * @param {Object} config A config object that sets properties on this grid.
40306  */
40307 Roo.grid.Calendar = function(container, config){
40308         // initialize the container
40309         this.container = Roo.get(container);
40310         this.container.update("");
40311         this.container.setStyle("overflow", "hidden");
40312     this.container.addClass('x-grid-container');
40313
40314     this.id = this.container.id;
40315
40316     Roo.apply(this, config);
40317     // check and correct shorthanded configs
40318     
40319     var rows = [];
40320     var d =1;
40321     for (var r = 0;r < 6;r++) {
40322         
40323         rows[r]=[];
40324         for (var c =0;c < 7;c++) {
40325             rows[r][c]= '';
40326         }
40327     }
40328     if (this.eventStore) {
40329         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40330         this.eventStore.on('load',this.onLoad, this);
40331         this.eventStore.on('beforeload',this.clearEvents, this);
40332          
40333     }
40334     
40335     this.dataSource = new Roo.data.Store({
40336             proxy: new Roo.data.MemoryProxy(rows),
40337             reader: new Roo.data.ArrayReader({}, [
40338                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40339     });
40340
40341     this.dataSource.load();
40342     this.ds = this.dataSource;
40343     this.ds.xmodule = this.xmodule || false;
40344     
40345     
40346     var cellRender = function(v,x,r)
40347     {
40348         return String.format(
40349             '<div class="fc-day  fc-widget-content"><div>' +
40350                 '<div class="fc-event-container"></div>' +
40351                 '<div class="fc-day-number">{0}</div>'+
40352                 
40353                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40354             '</div></div>', v);
40355     
40356     }
40357     
40358     
40359     this.colModel = new Roo.grid.ColumnModel( [
40360         {
40361             xtype: 'ColumnModel',
40362             xns: Roo.grid,
40363             dataIndex : 'weekday0',
40364             header : 'Sunday',
40365             renderer : cellRender
40366         },
40367         {
40368             xtype: 'ColumnModel',
40369             xns: Roo.grid,
40370             dataIndex : 'weekday1',
40371             header : 'Monday',
40372             renderer : cellRender
40373         },
40374         {
40375             xtype: 'ColumnModel',
40376             xns: Roo.grid,
40377             dataIndex : 'weekday2',
40378             header : 'Tuesday',
40379             renderer : cellRender
40380         },
40381         {
40382             xtype: 'ColumnModel',
40383             xns: Roo.grid,
40384             dataIndex : 'weekday3',
40385             header : 'Wednesday',
40386             renderer : cellRender
40387         },
40388         {
40389             xtype: 'ColumnModel',
40390             xns: Roo.grid,
40391             dataIndex : 'weekday4',
40392             header : 'Thursday',
40393             renderer : cellRender
40394         },
40395         {
40396             xtype: 'ColumnModel',
40397             xns: Roo.grid,
40398             dataIndex : 'weekday5',
40399             header : 'Friday',
40400             renderer : cellRender
40401         },
40402         {
40403             xtype: 'ColumnModel',
40404             xns: Roo.grid,
40405             dataIndex : 'weekday6',
40406             header : 'Saturday',
40407             renderer : cellRender
40408         }
40409     ]);
40410     this.cm = this.colModel;
40411     this.cm.xmodule = this.xmodule || false;
40412  
40413         
40414           
40415     //this.selModel = new Roo.grid.CellSelectionModel();
40416     //this.sm = this.selModel;
40417     //this.selModel.init(this);
40418     
40419     
40420     if(this.width){
40421         this.container.setWidth(this.width);
40422     }
40423
40424     if(this.height){
40425         this.container.setHeight(this.height);
40426     }
40427     /** @private */
40428         this.addEvents({
40429         // raw events
40430         /**
40431          * @event click
40432          * The raw click event for the entire grid.
40433          * @param {Roo.EventObject} e
40434          */
40435         "click" : true,
40436         /**
40437          * @event dblclick
40438          * The raw dblclick event for the entire grid.
40439          * @param {Roo.EventObject} e
40440          */
40441         "dblclick" : true,
40442         /**
40443          * @event contextmenu
40444          * The raw contextmenu event for the entire grid.
40445          * @param {Roo.EventObject} e
40446          */
40447         "contextmenu" : true,
40448         /**
40449          * @event mousedown
40450          * The raw mousedown event for the entire grid.
40451          * @param {Roo.EventObject} e
40452          */
40453         "mousedown" : true,
40454         /**
40455          * @event mouseup
40456          * The raw mouseup event for the entire grid.
40457          * @param {Roo.EventObject} e
40458          */
40459         "mouseup" : true,
40460         /**
40461          * @event mouseover
40462          * The raw mouseover event for the entire grid.
40463          * @param {Roo.EventObject} e
40464          */
40465         "mouseover" : true,
40466         /**
40467          * @event mouseout
40468          * The raw mouseout event for the entire grid.
40469          * @param {Roo.EventObject} e
40470          */
40471         "mouseout" : true,
40472         /**
40473          * @event keypress
40474          * The raw keypress event for the entire grid.
40475          * @param {Roo.EventObject} e
40476          */
40477         "keypress" : true,
40478         /**
40479          * @event keydown
40480          * The raw keydown event for the entire grid.
40481          * @param {Roo.EventObject} e
40482          */
40483         "keydown" : true,
40484
40485         // custom events
40486
40487         /**
40488          * @event cellclick
40489          * Fires when a cell is clicked
40490          * @param {Grid} this
40491          * @param {Number} rowIndex
40492          * @param {Number} columnIndex
40493          * @param {Roo.EventObject} e
40494          */
40495         "cellclick" : true,
40496         /**
40497          * @event celldblclick
40498          * Fires when a cell is double clicked
40499          * @param {Grid} this
40500          * @param {Number} rowIndex
40501          * @param {Number} columnIndex
40502          * @param {Roo.EventObject} e
40503          */
40504         "celldblclick" : true,
40505         /**
40506          * @event rowclick
40507          * Fires when a row is clicked
40508          * @param {Grid} this
40509          * @param {Number} rowIndex
40510          * @param {Roo.EventObject} e
40511          */
40512         "rowclick" : true,
40513         /**
40514          * @event rowdblclick
40515          * Fires when a row is double clicked
40516          * @param {Grid} this
40517          * @param {Number} rowIndex
40518          * @param {Roo.EventObject} e
40519          */
40520         "rowdblclick" : true,
40521         /**
40522          * @event headerclick
40523          * Fires when a header is clicked
40524          * @param {Grid} this
40525          * @param {Number} columnIndex
40526          * @param {Roo.EventObject} e
40527          */
40528         "headerclick" : true,
40529         /**
40530          * @event headerdblclick
40531          * Fires when a header cell is double clicked
40532          * @param {Grid} this
40533          * @param {Number} columnIndex
40534          * @param {Roo.EventObject} e
40535          */
40536         "headerdblclick" : true,
40537         /**
40538          * @event rowcontextmenu
40539          * Fires when a row is right clicked
40540          * @param {Grid} this
40541          * @param {Number} rowIndex
40542          * @param {Roo.EventObject} e
40543          */
40544         "rowcontextmenu" : true,
40545         /**
40546          * @event cellcontextmenu
40547          * Fires when a cell is right clicked
40548          * @param {Grid} this
40549          * @param {Number} rowIndex
40550          * @param {Number} cellIndex
40551          * @param {Roo.EventObject} e
40552          */
40553          "cellcontextmenu" : true,
40554         /**
40555          * @event headercontextmenu
40556          * Fires when a header is right clicked
40557          * @param {Grid} this
40558          * @param {Number} columnIndex
40559          * @param {Roo.EventObject} e
40560          */
40561         "headercontextmenu" : true,
40562         /**
40563          * @event bodyscroll
40564          * Fires when the body element is scrolled
40565          * @param {Number} scrollLeft
40566          * @param {Number} scrollTop
40567          */
40568         "bodyscroll" : true,
40569         /**
40570          * @event columnresize
40571          * Fires when the user resizes a column
40572          * @param {Number} columnIndex
40573          * @param {Number} newSize
40574          */
40575         "columnresize" : true,
40576         /**
40577          * @event columnmove
40578          * Fires when the user moves a column
40579          * @param {Number} oldIndex
40580          * @param {Number} newIndex
40581          */
40582         "columnmove" : true,
40583         /**
40584          * @event startdrag
40585          * Fires when row(s) start being dragged
40586          * @param {Grid} this
40587          * @param {Roo.GridDD} dd The drag drop object
40588          * @param {event} e The raw browser event
40589          */
40590         "startdrag" : true,
40591         /**
40592          * @event enddrag
40593          * Fires when a drag operation is complete
40594          * @param {Grid} this
40595          * @param {Roo.GridDD} dd The drag drop object
40596          * @param {event} e The raw browser event
40597          */
40598         "enddrag" : true,
40599         /**
40600          * @event dragdrop
40601          * Fires when dragged row(s) are dropped on a valid DD target
40602          * @param {Grid} this
40603          * @param {Roo.GridDD} dd The drag drop object
40604          * @param {String} targetId The target drag drop object
40605          * @param {event} e The raw browser event
40606          */
40607         "dragdrop" : true,
40608         /**
40609          * @event dragover
40610          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40611          * @param {Grid} this
40612          * @param {Roo.GridDD} dd The drag drop object
40613          * @param {String} targetId The target drag drop object
40614          * @param {event} e The raw browser event
40615          */
40616         "dragover" : true,
40617         /**
40618          * @event dragenter
40619          *  Fires when the dragged row(s) first cross another DD target while being dragged
40620          * @param {Grid} this
40621          * @param {Roo.GridDD} dd The drag drop object
40622          * @param {String} targetId The target drag drop object
40623          * @param {event} e The raw browser event
40624          */
40625         "dragenter" : true,
40626         /**
40627          * @event dragout
40628          * Fires when the dragged row(s) leave another DD target while being dragged
40629          * @param {Grid} this
40630          * @param {Roo.GridDD} dd The drag drop object
40631          * @param {String} targetId The target drag drop object
40632          * @param {event} e The raw browser event
40633          */
40634         "dragout" : true,
40635         /**
40636          * @event rowclass
40637          * Fires when a row is rendered, so you can change add a style to it.
40638          * @param {GridView} gridview   The grid view
40639          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40640          */
40641         'rowclass' : true,
40642
40643         /**
40644          * @event render
40645          * Fires when the grid is rendered
40646          * @param {Grid} grid
40647          */
40648         'render' : true,
40649             /**
40650              * @event select
40651              * Fires when a date is selected
40652              * @param {DatePicker} this
40653              * @param {Date} date The selected date
40654              */
40655         'select': true,
40656         /**
40657              * @event monthchange
40658              * Fires when the displayed month changes 
40659              * @param {DatePicker} this
40660              * @param {Date} date The selected month
40661              */
40662         'monthchange': true,
40663         /**
40664              * @event evententer
40665              * Fires when mouse over an event
40666              * @param {Calendar} this
40667              * @param {event} Event
40668              */
40669         'evententer': true,
40670         /**
40671              * @event eventleave
40672              * Fires when the mouse leaves an
40673              * @param {Calendar} this
40674              * @param {event}
40675              */
40676         'eventleave': true,
40677         /**
40678              * @event eventclick
40679              * Fires when the mouse click an
40680              * @param {Calendar} this
40681              * @param {event}
40682              */
40683         'eventclick': true,
40684         /**
40685              * @event eventrender
40686              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
40687              * @param {Calendar} this
40688              * @param {data} data to be modified
40689              */
40690         'eventrender': true
40691         
40692     });
40693
40694     Roo.grid.Grid.superclass.constructor.call(this);
40695     this.on('render', function() {
40696         this.view.el.addClass('x-grid-cal'); 
40697         
40698         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
40699
40700     },this);
40701     
40702     if (!Roo.grid.Calendar.style) {
40703         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
40704             
40705             
40706             '.x-grid-cal .x-grid-col' :  {
40707                 height: 'auto !important',
40708                 'vertical-align': 'top'
40709             },
40710             '.x-grid-cal  .fc-event-hori' : {
40711                 height: '14px'
40712             }
40713              
40714             
40715         }, Roo.id());
40716     }
40717
40718     
40719     
40720 };
40721 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
40722     /**
40723      * @cfg {Store} eventStore The store that loads events.
40724      */
40725     eventStore : 25,
40726
40727      
40728     activeDate : false,
40729     startDay : 0,
40730     autoWidth : true,
40731     monitorWindowResize : false,
40732
40733     
40734     resizeColumns : function() {
40735         var col = (this.view.el.getWidth() / 7) - 3;
40736         // loop through cols, and setWidth
40737         for(var i =0 ; i < 7 ; i++){
40738             this.cm.setColumnWidth(i, col);
40739         }
40740     },
40741      setDate :function(date) {
40742         
40743         Roo.log('setDate?');
40744         
40745         this.resizeColumns();
40746         var vd = this.activeDate;
40747         this.activeDate = date;
40748 //        if(vd && this.el){
40749 //            var t = date.getTime();
40750 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
40751 //                Roo.log('using add remove');
40752 //                
40753 //                this.fireEvent('monthchange', this, date);
40754 //                
40755 //                this.cells.removeClass("fc-state-highlight");
40756 //                this.cells.each(function(c){
40757 //                   if(c.dateValue == t){
40758 //                       c.addClass("fc-state-highlight");
40759 //                       setTimeout(function(){
40760 //                            try{c.dom.firstChild.focus();}catch(e){}
40761 //                       }, 50);
40762 //                       return false;
40763 //                   }
40764 //                   return true;
40765 //                });
40766 //                return;
40767 //            }
40768 //        }
40769         
40770         var days = date.getDaysInMonth();
40771         
40772         var firstOfMonth = date.getFirstDateOfMonth();
40773         var startingPos = firstOfMonth.getDay()-this.startDay;
40774         
40775         if(startingPos < this.startDay){
40776             startingPos += 7;
40777         }
40778         
40779         var pm = date.add(Date.MONTH, -1);
40780         var prevStart = pm.getDaysInMonth()-startingPos;
40781 //        
40782         
40783         
40784         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40785         
40786         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
40787         //this.cells.addClassOnOver('fc-state-hover');
40788         
40789         var cells = this.cells.elements;
40790         var textEls = this.textNodes;
40791         
40792         //Roo.each(cells, function(cell){
40793         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
40794         //});
40795         
40796         days += startingPos;
40797
40798         // convert everything to numbers so it's fast
40799         var day = 86400000;
40800         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
40801         //Roo.log(d);
40802         //Roo.log(pm);
40803         //Roo.log(prevStart);
40804         
40805         var today = new Date().clearTime().getTime();
40806         var sel = date.clearTime().getTime();
40807         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
40808         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
40809         var ddMatch = this.disabledDatesRE;
40810         var ddText = this.disabledDatesText;
40811         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
40812         var ddaysText = this.disabledDaysText;
40813         var format = this.format;
40814         
40815         var setCellClass = function(cal, cell){
40816             
40817             //Roo.log('set Cell Class');
40818             cell.title = "";
40819             var t = d.getTime();
40820             
40821             //Roo.log(d);
40822             
40823             
40824             cell.dateValue = t;
40825             if(t == today){
40826                 cell.className += " fc-today";
40827                 cell.className += " fc-state-highlight";
40828                 cell.title = cal.todayText;
40829             }
40830             if(t == sel){
40831                 // disable highlight in other month..
40832                 cell.className += " fc-state-highlight";
40833                 
40834             }
40835             // disabling
40836             if(t < min) {
40837                 //cell.className = " fc-state-disabled";
40838                 cell.title = cal.minText;
40839                 return;
40840             }
40841             if(t > max) {
40842                 //cell.className = " fc-state-disabled";
40843                 cell.title = cal.maxText;
40844                 return;
40845             }
40846             if(ddays){
40847                 if(ddays.indexOf(d.getDay()) != -1){
40848                     // cell.title = ddaysText;
40849                    // cell.className = " fc-state-disabled";
40850                 }
40851             }
40852             if(ddMatch && format){
40853                 var fvalue = d.dateFormat(format);
40854                 if(ddMatch.test(fvalue)){
40855                     cell.title = ddText.replace("%0", fvalue);
40856                    cell.className = " fc-state-disabled";
40857                 }
40858             }
40859             
40860             if (!cell.initialClassName) {
40861                 cell.initialClassName = cell.dom.className;
40862             }
40863             
40864             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
40865         };
40866
40867         var i = 0;
40868         
40869         for(; i < startingPos; i++) {
40870             cells[i].dayName =  (++prevStart);
40871             Roo.log(textEls[i]);
40872             d.setDate(d.getDate()+1);
40873             
40874             //cells[i].className = "fc-past fc-other-month";
40875             setCellClass(this, cells[i]);
40876         }
40877         
40878         var intDay = 0;
40879         
40880         for(; i < days; i++){
40881             intDay = i - startingPos + 1;
40882             cells[i].dayName =  (intDay);
40883             d.setDate(d.getDate()+1);
40884             
40885             cells[i].className = ''; // "x-date-active";
40886             setCellClass(this, cells[i]);
40887         }
40888         var extraDays = 0;
40889         
40890         for(; i < 42; i++) {
40891             //textEls[i].innerHTML = (++extraDays);
40892             
40893             d.setDate(d.getDate()+1);
40894             cells[i].dayName = (++extraDays);
40895             cells[i].className = "fc-future fc-other-month";
40896             setCellClass(this, cells[i]);
40897         }
40898         
40899         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
40900         
40901         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
40902         
40903         // this will cause all the cells to mis
40904         var rows= [];
40905         var i =0;
40906         for (var r = 0;r < 6;r++) {
40907             for (var c =0;c < 7;c++) {
40908                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
40909             }    
40910         }
40911         
40912         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40913         for(i=0;i<cells.length;i++) {
40914             
40915             this.cells.elements[i].dayName = cells[i].dayName ;
40916             this.cells.elements[i].className = cells[i].className;
40917             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
40918             this.cells.elements[i].title = cells[i].title ;
40919             this.cells.elements[i].dateValue = cells[i].dateValue ;
40920         }
40921         
40922         
40923         
40924         
40925         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
40926         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
40927         
40928         ////if(totalRows != 6){
40929             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
40930            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
40931        // }
40932         
40933         this.fireEvent('monthchange', this, date);
40934         
40935         
40936     },
40937  /**
40938      * Returns the grid's SelectionModel.
40939      * @return {SelectionModel}
40940      */
40941     getSelectionModel : function(){
40942         if(!this.selModel){
40943             this.selModel = new Roo.grid.CellSelectionModel();
40944         }
40945         return this.selModel;
40946     },
40947
40948     load: function() {
40949         this.eventStore.load()
40950         
40951         
40952         
40953     },
40954     
40955     findCell : function(dt) {
40956         dt = dt.clearTime().getTime();
40957         var ret = false;
40958         this.cells.each(function(c){
40959             //Roo.log("check " +c.dateValue + '?=' + dt);
40960             if(c.dateValue == dt){
40961                 ret = c;
40962                 return false;
40963             }
40964             return true;
40965         });
40966         
40967         return ret;
40968     },
40969     
40970     findCells : function(rec) {
40971         var s = rec.data.start_dt.clone().clearTime().getTime();
40972        // Roo.log(s);
40973         var e= rec.data.end_dt.clone().clearTime().getTime();
40974        // Roo.log(e);
40975         var ret = [];
40976         this.cells.each(function(c){
40977              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
40978             
40979             if(c.dateValue > e){
40980                 return ;
40981             }
40982             if(c.dateValue < s){
40983                 return ;
40984             }
40985             ret.push(c);
40986         });
40987         
40988         return ret;    
40989     },
40990     
40991     findBestRow: function(cells)
40992     {
40993         var ret = 0;
40994         
40995         for (var i =0 ; i < cells.length;i++) {
40996             ret  = Math.max(cells[i].rows || 0,ret);
40997         }
40998         return ret;
40999         
41000     },
41001     
41002     
41003     addItem : function(rec)
41004     {
41005         // look for vertical location slot in
41006         var cells = this.findCells(rec);
41007         
41008         rec.row = this.findBestRow(cells);
41009         
41010         // work out the location.
41011         
41012         var crow = false;
41013         var rows = [];
41014         for(var i =0; i < cells.length; i++) {
41015             if (!crow) {
41016                 crow = {
41017                     start : cells[i],
41018                     end :  cells[i]
41019                 };
41020                 continue;
41021             }
41022             if (crow.start.getY() == cells[i].getY()) {
41023                 // on same row.
41024                 crow.end = cells[i];
41025                 continue;
41026             }
41027             // different row.
41028             rows.push(crow);
41029             crow = {
41030                 start: cells[i],
41031                 end : cells[i]
41032             };
41033             
41034         }
41035         
41036         rows.push(crow);
41037         rec.els = [];
41038         rec.rows = rows;
41039         rec.cells = cells;
41040         for (var i = 0; i < cells.length;i++) {
41041             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41042             
41043         }
41044         
41045         
41046     },
41047     
41048     clearEvents: function() {
41049         
41050         if (!this.eventStore.getCount()) {
41051             return;
41052         }
41053         // reset number of rows in cells.
41054         Roo.each(this.cells.elements, function(c){
41055             c.rows = 0;
41056         });
41057         
41058         this.eventStore.each(function(e) {
41059             this.clearEvent(e);
41060         },this);
41061         
41062     },
41063     
41064     clearEvent : function(ev)
41065     {
41066         if (ev.els) {
41067             Roo.each(ev.els, function(el) {
41068                 el.un('mouseenter' ,this.onEventEnter, this);
41069                 el.un('mouseleave' ,this.onEventLeave, this);
41070                 el.remove();
41071             },this);
41072             ev.els = [];
41073         }
41074     },
41075     
41076     
41077     renderEvent : function(ev,ctr) {
41078         if (!ctr) {
41079              ctr = this.view.el.select('.fc-event-container',true).first();
41080         }
41081         
41082          
41083         this.clearEvent(ev);
41084             //code
41085        
41086         
41087         
41088         ev.els = [];
41089         var cells = ev.cells;
41090         var rows = ev.rows;
41091         this.fireEvent('eventrender', this, ev);
41092         
41093         for(var i =0; i < rows.length; i++) {
41094             
41095             cls = '';
41096             if (i == 0) {
41097                 cls += ' fc-event-start';
41098             }
41099             if ((i+1) == rows.length) {
41100                 cls += ' fc-event-end';
41101             }
41102             
41103             //Roo.log(ev.data);
41104             // how many rows should it span..
41105             var cg = this.eventTmpl.append(ctr,Roo.apply({
41106                 fccls : cls
41107                 
41108             }, ev.data) , true);
41109             
41110             
41111             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41112             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41113             cg.on('click', this.onEventClick, this, ev);
41114             
41115             ev.els.push(cg);
41116             
41117             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41118             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41119             //Roo.log(cg);
41120              
41121             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41122             cg.setWidth(ebox.right - sbox.x -2);
41123         }
41124     },
41125     
41126     renderEvents: function()
41127     {   
41128         // first make sure there is enough space..
41129         
41130         if (!this.eventTmpl) {
41131             this.eventTmpl = new Roo.Template(
41132                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41133                     '<div class="fc-event-inner">' +
41134                         '<span class="fc-event-time">{time}</span>' +
41135                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41136                     '</div>' +
41137                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41138                 '</div>'
41139             );
41140                 
41141         }
41142                
41143         
41144         
41145         this.cells.each(function(c) {
41146             //Roo.log(c.select('.fc-day-content div',true).first());
41147             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41148         });
41149         
41150         var ctr = this.view.el.select('.fc-event-container',true).first();
41151         
41152         var cls;
41153         this.eventStore.each(function(ev){
41154             
41155             this.renderEvent(ev);
41156              
41157              
41158         }, this);
41159         this.view.layout();
41160         
41161     },
41162     
41163     onEventEnter: function (e, el,event,d) {
41164         this.fireEvent('evententer', this, el, event);
41165     },
41166     
41167     onEventLeave: function (e, el,event,d) {
41168         this.fireEvent('eventleave', this, el, event);
41169     },
41170     
41171     onEventClick: function (e, el,event,d) {
41172         this.fireEvent('eventclick', this, el, event);
41173     },
41174     
41175     onMonthChange: function () {
41176         this.store.load();
41177     },
41178     
41179     onLoad: function () {
41180         
41181         //Roo.log('calendar onload');
41182 //         
41183         if(this.eventStore.getCount() > 0){
41184             
41185            
41186             
41187             this.eventStore.each(function(d){
41188                 
41189                 
41190                 // FIXME..
41191                 var add =   d.data;
41192                 if (typeof(add.end_dt) == 'undefined')  {
41193                     Roo.log("Missing End time in calendar data: ");
41194                     Roo.log(d);
41195                     return;
41196                 }
41197                 if (typeof(add.start_dt) == 'undefined')  {
41198                     Roo.log("Missing Start time in calendar data: ");
41199                     Roo.log(d);
41200                     return;
41201                 }
41202                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41203                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41204                 add.id = add.id || d.id;
41205                 add.title = add.title || '??';
41206                 
41207                 this.addItem(d);
41208                 
41209              
41210             },this);
41211         }
41212         
41213         this.renderEvents();
41214     }
41215     
41216
41217 });
41218 /*
41219  grid : {
41220                 xtype: 'Grid',
41221                 xns: Roo.grid,
41222                 listeners : {
41223                     render : function ()
41224                     {
41225                         _this.grid = this;
41226                         
41227                         if (!this.view.el.hasClass('course-timesheet')) {
41228                             this.view.el.addClass('course-timesheet');
41229                         }
41230                         if (this.tsStyle) {
41231                             this.ds.load({});
41232                             return; 
41233                         }
41234                         Roo.log('width');
41235                         Roo.log(_this.grid.view.el.getWidth());
41236                         
41237                         
41238                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41239                             '.course-timesheet .x-grid-row' : {
41240                                 height: '80px'
41241                             },
41242                             '.x-grid-row td' : {
41243                                 'vertical-align' : 0
41244                             },
41245                             '.course-edit-link' : {
41246                                 'color' : 'blue',
41247                                 'text-overflow' : 'ellipsis',
41248                                 'overflow' : 'hidden',
41249                                 'white-space' : 'nowrap',
41250                                 'cursor' : 'pointer'
41251                             },
41252                             '.sub-link' : {
41253                                 'color' : 'green'
41254                             },
41255                             '.de-act-sup-link' : {
41256                                 'color' : 'purple',
41257                                 'text-decoration' : 'line-through'
41258                             },
41259                             '.de-act-link' : {
41260                                 'color' : 'red',
41261                                 'text-decoration' : 'line-through'
41262                             },
41263                             '.course-timesheet .course-highlight' : {
41264                                 'border-top-style': 'dashed !important',
41265                                 'border-bottom-bottom': 'dashed !important'
41266                             },
41267                             '.course-timesheet .course-item' : {
41268                                 'font-family'   : 'tahoma, arial, helvetica',
41269                                 'font-size'     : '11px',
41270                                 'overflow'      : 'hidden',
41271                                 'padding-left'  : '10px',
41272                                 'padding-right' : '10px',
41273                                 'padding-top' : '10px' 
41274                             }
41275                             
41276                         }, Roo.id());
41277                                 this.ds.load({});
41278                     }
41279                 },
41280                 autoWidth : true,
41281                 monitorWindowResize : false,
41282                 cellrenderer : function(v,x,r)
41283                 {
41284                     return v;
41285                 },
41286                 sm : {
41287                     xtype: 'CellSelectionModel',
41288                     xns: Roo.grid
41289                 },
41290                 dataSource : {
41291                     xtype: 'Store',
41292                     xns: Roo.data,
41293                     listeners : {
41294                         beforeload : function (_self, options)
41295                         {
41296                             options.params = options.params || {};
41297                             options.params._month = _this.monthField.getValue();
41298                             options.params.limit = 9999;
41299                             options.params['sort'] = 'when_dt';    
41300                             options.params['dir'] = 'ASC';    
41301                             this.proxy.loadResponse = this.loadResponse;
41302                             Roo.log("load?");
41303                             //this.addColumns();
41304                         },
41305                         load : function (_self, records, options)
41306                         {
41307                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41308                                 // if you click on the translation.. you can edit it...
41309                                 var el = Roo.get(this);
41310                                 var id = el.dom.getAttribute('data-id');
41311                                 var d = el.dom.getAttribute('data-date');
41312                                 var t = el.dom.getAttribute('data-time');
41313                                 //var id = this.child('span').dom.textContent;
41314                                 
41315                                 //Roo.log(this);
41316                                 Pman.Dialog.CourseCalendar.show({
41317                                     id : id,
41318                                     when_d : d,
41319                                     when_t : t,
41320                                     productitem_active : id ? 1 : 0
41321                                 }, function() {
41322                                     _this.grid.ds.load({});
41323                                 });
41324                            
41325                            });
41326                            
41327                            _this.panel.fireEvent('resize', [ '', '' ]);
41328                         }
41329                     },
41330                     loadResponse : function(o, success, response){
41331                             // this is overridden on before load..
41332                             
41333                             Roo.log("our code?");       
41334                             //Roo.log(success);
41335                             //Roo.log(response)
41336                             delete this.activeRequest;
41337                             if(!success){
41338                                 this.fireEvent("loadexception", this, o, response);
41339                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41340                                 return;
41341                             }
41342                             var result;
41343                             try {
41344                                 result = o.reader.read(response);
41345                             }catch(e){
41346                                 Roo.log("load exception?");
41347                                 this.fireEvent("loadexception", this, o, response, e);
41348                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41349                                 return;
41350                             }
41351                             Roo.log("ready...");        
41352                             // loop through result.records;
41353                             // and set this.tdate[date] = [] << array of records..
41354                             _this.tdata  = {};
41355                             Roo.each(result.records, function(r){
41356                                 //Roo.log(r.data);
41357                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41358                                     _this.tdata[r.data.when_dt.format('j')] = [];
41359                                 }
41360                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41361                             });
41362                             
41363                             //Roo.log(_this.tdata);
41364                             
41365                             result.records = [];
41366                             result.totalRecords = 6;
41367                     
41368                             // let's generate some duumy records for the rows.
41369                             //var st = _this.dateField.getValue();
41370                             
41371                             // work out monday..
41372                             //st = st.add(Date.DAY, -1 * st.format('w'));
41373                             
41374                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41375                             
41376                             var firstOfMonth = date.getFirstDayOfMonth();
41377                             var days = date.getDaysInMonth();
41378                             var d = 1;
41379                             var firstAdded = false;
41380                             for (var i = 0; i < result.totalRecords ; i++) {
41381                                 //var d= st.add(Date.DAY, i);
41382                                 var row = {};
41383                                 var added = 0;
41384                                 for(var w = 0 ; w < 7 ; w++){
41385                                     if(!firstAdded && firstOfMonth != w){
41386                                         continue;
41387                                     }
41388                                     if(d > days){
41389                                         continue;
41390                                     }
41391                                     firstAdded = true;
41392                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41393                                     row['weekday'+w] = String.format(
41394                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41395                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41396                                                     d,
41397                                                     date.format('Y-m-')+dd
41398                                                 );
41399                                     added++;
41400                                     if(typeof(_this.tdata[d]) != 'undefined'){
41401                                         Roo.each(_this.tdata[d], function(r){
41402                                             var is_sub = '';
41403                                             var deactive = '';
41404                                             var id = r.id;
41405                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41406                                             if(r.parent_id*1>0){
41407                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41408                                                 id = r.parent_id;
41409                                             }
41410                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41411                                                 deactive = 'de-act-link';
41412                                             }
41413                                             
41414                                             row['weekday'+w] += String.format(
41415                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41416                                                     id, //0
41417                                                     r.product_id_name, //1
41418                                                     r.when_dt.format('h:ia'), //2
41419                                                     is_sub, //3
41420                                                     deactive, //4
41421                                                     desc // 5
41422                                             );
41423                                         });
41424                                     }
41425                                     d++;
41426                                 }
41427                                 
41428                                 // only do this if something added..
41429                                 if(added > 0){ 
41430                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41431                                 }
41432                                 
41433                                 
41434                                 // push it twice. (second one with an hour..
41435                                 
41436                             }
41437                             //Roo.log(result);
41438                             this.fireEvent("load", this, o, o.request.arg);
41439                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41440                         },
41441                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41442                     proxy : {
41443                         xtype: 'HttpProxy',
41444                         xns: Roo.data,
41445                         method : 'GET',
41446                         url : baseURL + '/Roo/Shop_course.php'
41447                     },
41448                     reader : {
41449                         xtype: 'JsonReader',
41450                         xns: Roo.data,
41451                         id : 'id',
41452                         fields : [
41453                             {
41454                                 'name': 'id',
41455                                 'type': 'int'
41456                             },
41457                             {
41458                                 'name': 'when_dt',
41459                                 'type': 'string'
41460                             },
41461                             {
41462                                 'name': 'end_dt',
41463                                 'type': 'string'
41464                             },
41465                             {
41466                                 'name': 'parent_id',
41467                                 'type': 'int'
41468                             },
41469                             {
41470                                 'name': 'product_id',
41471                                 'type': 'int'
41472                             },
41473                             {
41474                                 'name': 'productitem_id',
41475                                 'type': 'int'
41476                             },
41477                             {
41478                                 'name': 'guid',
41479                                 'type': 'int'
41480                             }
41481                         ]
41482                     }
41483                 },
41484                 toolbar : {
41485                     xtype: 'Toolbar',
41486                     xns: Roo,
41487                     items : [
41488                         {
41489                             xtype: 'Button',
41490                             xns: Roo.Toolbar,
41491                             listeners : {
41492                                 click : function (_self, e)
41493                                 {
41494                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41495                                     sd.setMonth(sd.getMonth()-1);
41496                                     _this.monthField.setValue(sd.format('Y-m-d'));
41497                                     _this.grid.ds.load({});
41498                                 }
41499                             },
41500                             text : "Back"
41501                         },
41502                         {
41503                             xtype: 'Separator',
41504                             xns: Roo.Toolbar
41505                         },
41506                         {
41507                             xtype: 'MonthField',
41508                             xns: Roo.form,
41509                             listeners : {
41510                                 render : function (_self)
41511                                 {
41512                                     _this.monthField = _self;
41513                                    // _this.monthField.set  today
41514                                 },
41515                                 select : function (combo, date)
41516                                 {
41517                                     _this.grid.ds.load({});
41518                                 }
41519                             },
41520                             value : (function() { return new Date(); })()
41521                         },
41522                         {
41523                             xtype: 'Separator',
41524                             xns: Roo.Toolbar
41525                         },
41526                         {
41527                             xtype: 'TextItem',
41528                             xns: Roo.Toolbar,
41529                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41530                         },
41531                         {
41532                             xtype: 'Fill',
41533                             xns: Roo.Toolbar
41534                         },
41535                         {
41536                             xtype: 'Button',
41537                             xns: Roo.Toolbar,
41538                             listeners : {
41539                                 click : function (_self, e)
41540                                 {
41541                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41542                                     sd.setMonth(sd.getMonth()+1);
41543                                     _this.monthField.setValue(sd.format('Y-m-d'));
41544                                     _this.grid.ds.load({});
41545                                 }
41546                             },
41547                             text : "Next"
41548                         }
41549                     ]
41550                 },
41551                  
41552             }
41553         };
41554         
41555         *//*
41556  * Based on:
41557  * Ext JS Library 1.1.1
41558  * Copyright(c) 2006-2007, Ext JS, LLC.
41559  *
41560  * Originally Released Under LGPL - original licence link has changed is not relivant.
41561  *
41562  * Fork - LGPL
41563  * <script type="text/javascript">
41564  */
41565  
41566 /**
41567  * @class Roo.LoadMask
41568  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41569  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41570  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41571  * element's UpdateManager load indicator and will be destroyed after the initial load.
41572  * @constructor
41573  * Create a new LoadMask
41574  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41575  * @param {Object} config The config object
41576  */
41577 Roo.LoadMask = function(el, config){
41578     this.el = Roo.get(el);
41579     Roo.apply(this, config);
41580     if(this.store){
41581         this.store.on('beforeload', this.onBeforeLoad, this);
41582         this.store.on('load', this.onLoad, this);
41583         this.store.on('loadexception', this.onLoadException, this);
41584         this.removeMask = false;
41585     }else{
41586         var um = this.el.getUpdateManager();
41587         um.showLoadIndicator = false; // disable the default indicator
41588         um.on('beforeupdate', this.onBeforeLoad, this);
41589         um.on('update', this.onLoad, this);
41590         um.on('failure', this.onLoad, this);
41591         this.removeMask = true;
41592     }
41593 };
41594
41595 Roo.LoadMask.prototype = {
41596     /**
41597      * @cfg {Boolean} removeMask
41598      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41599      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41600      */
41601     /**
41602      * @cfg {String} msg
41603      * The text to display in a centered loading message box (defaults to 'Loading...')
41604      */
41605     msg : 'Loading...',
41606     /**
41607      * @cfg {String} msgCls
41608      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41609      */
41610     msgCls : 'x-mask-loading',
41611
41612     /**
41613      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41614      * @type Boolean
41615      */
41616     disabled: false,
41617
41618     /**
41619      * Disables the mask to prevent it from being displayed
41620      */
41621     disable : function(){
41622        this.disabled = true;
41623     },
41624
41625     /**
41626      * Enables the mask so that it can be displayed
41627      */
41628     enable : function(){
41629         this.disabled = false;
41630     },
41631     
41632     onLoadException : function()
41633     {
41634         Roo.log(arguments);
41635         
41636         if (typeof(arguments[3]) != 'undefined') {
41637             Roo.MessageBox.alert("Error loading",arguments[3]);
41638         } 
41639         /*
41640         try {
41641             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41642                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41643             }   
41644         } catch(e) {
41645             
41646         }
41647         */
41648     
41649         
41650         
41651         this.el.unmask(this.removeMask);
41652     },
41653     // private
41654     onLoad : function()
41655     {
41656         this.el.unmask(this.removeMask);
41657     },
41658
41659     // private
41660     onBeforeLoad : function(){
41661         if(!this.disabled){
41662             this.el.mask(this.msg, this.msgCls);
41663         }
41664     },
41665
41666     // private
41667     destroy : function(){
41668         if(this.store){
41669             this.store.un('beforeload', this.onBeforeLoad, this);
41670             this.store.un('load', this.onLoad, this);
41671             this.store.un('loadexception', this.onLoadException, this);
41672         }else{
41673             var um = this.el.getUpdateManager();
41674             um.un('beforeupdate', this.onBeforeLoad, this);
41675             um.un('update', this.onLoad, this);
41676             um.un('failure', this.onLoad, this);
41677         }
41678     }
41679 };/*
41680  * Based on:
41681  * Ext JS Library 1.1.1
41682  * Copyright(c) 2006-2007, Ext JS, LLC.
41683  *
41684  * Originally Released Under LGPL - original licence link has changed is not relivant.
41685  *
41686  * Fork - LGPL
41687  * <script type="text/javascript">
41688  */
41689
41690
41691 /**
41692  * @class Roo.XTemplate
41693  * @extends Roo.Template
41694  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
41695 <pre><code>
41696 var t = new Roo.XTemplate(
41697         '&lt;select name="{name}"&gt;',
41698                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
41699         '&lt;/select&gt;'
41700 );
41701  
41702 // then append, applying the master template values
41703  </code></pre>
41704  *
41705  * Supported features:
41706  *
41707  *  Tags:
41708
41709 <pre><code>
41710       {a_variable} - output encoded.
41711       {a_variable.format:("Y-m-d")} - call a method on the variable
41712       {a_variable:raw} - unencoded output
41713       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
41714       {a_variable:this.method_on_template(...)} - call a method on the template object.
41715  
41716 </code></pre>
41717  *  The tpl tag:
41718 <pre><code>
41719         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
41720         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
41721         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
41722         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
41723   
41724         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
41725         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
41726 </code></pre>
41727  *      
41728  */
41729 Roo.XTemplate = function()
41730 {
41731     Roo.XTemplate.superclass.constructor.apply(this, arguments);
41732     if (this.html) {
41733         this.compile();
41734     }
41735 };
41736
41737
41738 Roo.extend(Roo.XTemplate, Roo.Template, {
41739
41740     /**
41741      * The various sub templates
41742      */
41743     tpls : false,
41744     /**
41745      *
41746      * basic tag replacing syntax
41747      * WORD:WORD()
41748      *
41749      * // you can fake an object call by doing this
41750      *  x.t:(test,tesT) 
41751      * 
41752      */
41753     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
41754
41755     /**
41756      * compile the template
41757      *
41758      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
41759      *
41760      */
41761     compile: function()
41762     {
41763         var s = this.html;
41764      
41765         s = ['<tpl>', s, '</tpl>'].join('');
41766     
41767         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
41768             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
41769             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
41770             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
41771             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
41772             m,
41773             id     = 0,
41774             tpls   = [];
41775     
41776         while(true == !!(m = s.match(re))){
41777             var forMatch   = m[0].match(nameRe),
41778                 ifMatch   = m[0].match(ifRe),
41779                 execMatch   = m[0].match(execRe),
41780                 namedMatch   = m[0].match(namedRe),
41781                 
41782                 exp  = null, 
41783                 fn   = null,
41784                 exec = null,
41785                 name = forMatch && forMatch[1] ? forMatch[1] : '';
41786                 
41787             if (ifMatch) {
41788                 // if - puts fn into test..
41789                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
41790                 if(exp){
41791                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
41792                 }
41793             }
41794             
41795             if (execMatch) {
41796                 // exec - calls a function... returns empty if true is  returned.
41797                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
41798                 if(exp){
41799                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
41800                 }
41801             }
41802             
41803             
41804             if (name) {
41805                 // for = 
41806                 switch(name){
41807                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
41808                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
41809                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
41810                 }
41811             }
41812             var uid = namedMatch ? namedMatch[1] : id;
41813             
41814             
41815             tpls.push({
41816                 id:     namedMatch ? namedMatch[1] : id,
41817                 target: name,
41818                 exec:   exec,
41819                 test:   fn,
41820                 body:   m[1] || ''
41821             });
41822             if (namedMatch) {
41823                 s = s.replace(m[0], '');
41824             } else { 
41825                 s = s.replace(m[0], '{xtpl'+ id + '}');
41826             }
41827             ++id;
41828         }
41829         this.tpls = [];
41830         for(var i = tpls.length-1; i >= 0; --i){
41831             this.compileTpl(tpls[i]);
41832             this.tpls[tpls[i].id] = tpls[i];
41833         }
41834         this.master = tpls[tpls.length-1];
41835         return this;
41836     },
41837     /**
41838      * same as applyTemplate, except it's done to one of the subTemplates
41839      * when using named templates, you can do:
41840      *
41841      * var str = pl.applySubTemplate('your-name', values);
41842      *
41843      * 
41844      * @param {Number} id of the template
41845      * @param {Object} values to apply to template
41846      * @param {Object} parent (normaly the instance of this object)
41847      */
41848     applySubTemplate : function(id, values, parent)
41849     {
41850         
41851         
41852         var t = this.tpls[id];
41853         
41854         
41855         try { 
41856             if(t.test && !t.test.call(this, values, parent)){
41857                 return '';
41858             }
41859         } catch(e) {
41860             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
41861             Roo.log(e.toString());
41862             Roo.log(t.test);
41863             return ''
41864         }
41865         try { 
41866             
41867             if(t.exec && t.exec.call(this, values, parent)){
41868                 return '';
41869             }
41870         } catch(e) {
41871             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
41872             Roo.log(e.toString());
41873             Roo.log(t.exec);
41874             return ''
41875         }
41876         try {
41877             var vs = t.target ? t.target.call(this, values, parent) : values;
41878             parent = t.target ? values : parent;
41879             if(t.target && vs instanceof Array){
41880                 var buf = [];
41881                 for(var i = 0, len = vs.length; i < len; i++){
41882                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
41883                 }
41884                 return buf.join('');
41885             }
41886             return t.compiled.call(this, vs, parent);
41887         } catch (e) {
41888             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
41889             Roo.log(e.toString());
41890             Roo.log(t.compiled);
41891             return '';
41892         }
41893     },
41894
41895     compileTpl : function(tpl)
41896     {
41897         var fm = Roo.util.Format;
41898         var useF = this.disableFormats !== true;
41899         var sep = Roo.isGecko ? "+" : ",";
41900         var undef = function(str) {
41901             Roo.log("Property not found :"  + str);
41902             return '';
41903         };
41904         
41905         var fn = function(m, name, format, args)
41906         {
41907             //Roo.log(arguments);
41908             args = args ? args.replace(/\\'/g,"'") : args;
41909             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
41910             if (typeof(format) == 'undefined') {
41911                 format= 'htmlEncode';
41912             }
41913             if (format == 'raw' ) {
41914                 format = false;
41915             }
41916             
41917             if(name.substr(0, 4) == 'xtpl'){
41918                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
41919             }
41920             
41921             // build an array of options to determine if value is undefined..
41922             
41923             // basically get 'xxxx.yyyy' then do
41924             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
41925             //    (function () { Roo.log("Property not found"); return ''; })() :
41926             //    ......
41927             
41928             var udef_ar = [];
41929             var lookfor = '';
41930             Roo.each(name.split('.'), function(st) {
41931                 lookfor += (lookfor.length ? '.': '') + st;
41932                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
41933             });
41934             
41935             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
41936             
41937             
41938             if(format && useF){
41939                 
41940                 args = args ? ',' + args : "";
41941                  
41942                 if(format.substr(0, 5) != "this."){
41943                     format = "fm." + format + '(';
41944                 }else{
41945                     format = 'this.call("'+ format.substr(5) + '", ';
41946                     args = ", values";
41947                 }
41948                 
41949                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
41950             }
41951              
41952             if (args.length) {
41953                 // called with xxyx.yuu:(test,test)
41954                 // change to ()
41955                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
41956             }
41957             // raw.. - :raw modifier..
41958             return "'"+ sep + udef_st  + name + ")"+sep+"'";
41959             
41960         };
41961         var body;
41962         // branched to use + in gecko and [].join() in others
41963         if(Roo.isGecko){
41964             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
41965                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
41966                     "';};};";
41967         }else{
41968             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
41969             body.push(tpl.body.replace(/(\r\n|\n)/g,
41970                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
41971             body.push("'].join('');};};");
41972             body = body.join('');
41973         }
41974         
41975         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
41976        
41977         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
41978         eval(body);
41979         
41980         return this;
41981     },
41982
41983     applyTemplate : function(values){
41984         return this.master.compiled.call(this, values, {});
41985         //var s = this.subs;
41986     },
41987
41988     apply : function(){
41989         return this.applyTemplate.apply(this, arguments);
41990     }
41991
41992  });
41993
41994 Roo.XTemplate.from = function(el){
41995     el = Roo.getDom(el);
41996     return new Roo.XTemplate(el.value || el.innerHTML);
41997 };