Roo/grid/GridView.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)) {
4462             val = 0;
4463         }
4464         return val;
4465     },
4466     
4467     /**
4468      * Integer sorting
4469      * @param {Mixed} s The value being converted
4470      * @return {Number} The comparison value
4471      */
4472     asInt : function(s) {
4473         var val = parseInt(String(s).replace(/,/g, ""));
4474         if(isNaN(val)) {
4475             val = 0;
4476         }
4477         return val;
4478     }
4479 };/*
4480  * Based on:
4481  * Ext JS Library 1.1.1
4482  * Copyright(c) 2006-2007, Ext JS, LLC.
4483  *
4484  * Originally Released Under LGPL - original licence link has changed is not relivant.
4485  *
4486  * Fork - LGPL
4487  * <script type="text/javascript">
4488  */
4489
4490 /**
4491 * @class Roo.data.Record
4492  * Instances of this class encapsulate both record <em>definition</em> information, and record
4493  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4494  * to access Records cached in an {@link Roo.data.Store} object.<br>
4495  * <p>
4496  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4497  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4498  * objects.<br>
4499  * <p>
4500  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4501  * @constructor
4502  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4503  * {@link #create}. The parameters are the same.
4504  * @param {Array} data An associative Array of data values keyed by the field name.
4505  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4506  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4507  * not specified an integer id is generated.
4508  */
4509 Roo.data.Record = function(data, id){
4510     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4511     this.data = data;
4512 };
4513
4514 /**
4515  * Generate a constructor for a specific record layout.
4516  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4517  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4518  * Each field definition object may contain the following properties: <ul>
4519  * <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,
4520  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4521  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4522  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4523  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4524  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4525  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4526  * this may be omitted.</p></li>
4527  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4528  * <ul><li>auto (Default, implies no conversion)</li>
4529  * <li>string</li>
4530  * <li>int</li>
4531  * <li>float</li>
4532  * <li>boolean</li>
4533  * <li>date</li></ul></p></li>
4534  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4535  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4536  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4537  * by the Reader into an object that will be stored in the Record. It is passed the
4538  * following parameters:<ul>
4539  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4540  * </ul></p></li>
4541  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4542  * </ul>
4543  * <br>usage:<br><pre><code>
4544 var TopicRecord = Roo.data.Record.create(
4545     {name: 'title', mapping: 'topic_title'},
4546     {name: 'author', mapping: 'username'},
4547     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4548     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4549     {name: 'lastPoster', mapping: 'user2'},
4550     {name: 'excerpt', mapping: 'post_text'}
4551 );
4552
4553 var myNewRecord = new TopicRecord({
4554     title: 'Do my job please',
4555     author: 'noobie',
4556     totalPosts: 1,
4557     lastPost: new Date(),
4558     lastPoster: 'Animal',
4559     excerpt: 'No way dude!'
4560 });
4561 myStore.add(myNewRecord);
4562 </code></pre>
4563  * @method create
4564  * @static
4565  */
4566 Roo.data.Record.create = function(o){
4567     var f = function(){
4568         f.superclass.constructor.apply(this, arguments);
4569     };
4570     Roo.extend(f, Roo.data.Record);
4571     var p = f.prototype;
4572     p.fields = new Roo.util.MixedCollection(false, function(field){
4573         return field.name;
4574     });
4575     for(var i = 0, len = o.length; i < len; i++){
4576         p.fields.add(new Roo.data.Field(o[i]));
4577     }
4578     f.getField = function(name){
4579         return p.fields.get(name);  
4580     };
4581     return f;
4582 };
4583
4584 Roo.data.Record.AUTO_ID = 1000;
4585 Roo.data.Record.EDIT = 'edit';
4586 Roo.data.Record.REJECT = 'reject';
4587 Roo.data.Record.COMMIT = 'commit';
4588
4589 Roo.data.Record.prototype = {
4590     /**
4591      * Readonly flag - true if this record has been modified.
4592      * @type Boolean
4593      */
4594     dirty : false,
4595     editing : false,
4596     error: null,
4597     modified: null,
4598
4599     // private
4600     join : function(store){
4601         this.store = store;
4602     },
4603
4604     /**
4605      * Set the named field to the specified value.
4606      * @param {String} name The name of the field to set.
4607      * @param {Object} value The value to set the field to.
4608      */
4609     set : function(name, value){
4610         if(this.data[name] == value){
4611             return;
4612         }
4613         this.dirty = true;
4614         if(!this.modified){
4615             this.modified = {};
4616         }
4617         if(typeof this.modified[name] == 'undefined'){
4618             this.modified[name] = this.data[name];
4619         }
4620         this.data[name] = value;
4621         if(!this.editing && this.store){
4622             this.store.afterEdit(this);
4623         }       
4624     },
4625
4626     /**
4627      * Get the value of the named field.
4628      * @param {String} name The name of the field to get the value of.
4629      * @return {Object} The value of the field.
4630      */
4631     get : function(name){
4632         return this.data[name]; 
4633     },
4634
4635     // private
4636     beginEdit : function(){
4637         this.editing = true;
4638         this.modified = {}; 
4639     },
4640
4641     // private
4642     cancelEdit : function(){
4643         this.editing = false;
4644         delete this.modified;
4645     },
4646
4647     // private
4648     endEdit : function(){
4649         this.editing = false;
4650         if(this.dirty && this.store){
4651             this.store.afterEdit(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Rejects all changes made to the Record since either creation, or the last commit operation.
4658      * Modified fields are reverted to their original values.
4659      * <p>
4660      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4661      * of reject operations.
4662      */
4663     reject : function(){
4664         var m = this.modified;
4665         for(var n in m){
4666             if(typeof m[n] != "function"){
4667                 this.data[n] = m[n];
4668             }
4669         }
4670         this.dirty = false;
4671         delete this.modified;
4672         this.editing = false;
4673         if(this.store){
4674             this.store.afterReject(this);
4675         }
4676     },
4677
4678     /**
4679      * Usually called by the {@link Roo.data.Store} which owns the Record.
4680      * Commits all changes made to the Record since either creation, or the last commit operation.
4681      * <p>
4682      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4683      * of commit operations.
4684      */
4685     commit : function(){
4686         this.dirty = false;
4687         delete this.modified;
4688         this.editing = false;
4689         if(this.store){
4690             this.store.afterCommit(this);
4691         }
4692     },
4693
4694     // private
4695     hasError : function(){
4696         return this.error != null;
4697     },
4698
4699     // private
4700     clearError : function(){
4701         this.error = null;
4702     },
4703
4704     /**
4705      * Creates a copy of this record.
4706      * @param {String} id (optional) A new record id if you don't want to use this record's id
4707      * @return {Record}
4708      */
4709     copy : function(newId) {
4710         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4711     }
4712 };/*
4713  * Based on:
4714  * Ext JS Library 1.1.1
4715  * Copyright(c) 2006-2007, Ext JS, LLC.
4716  *
4717  * Originally Released Under LGPL - original licence link has changed is not relivant.
4718  *
4719  * Fork - LGPL
4720  * <script type="text/javascript">
4721  */
4722
4723
4724
4725 /**
4726  * @class Roo.data.Store
4727  * @extends Roo.util.Observable
4728  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4729  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4730  * <p>
4731  * 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
4732  * has no knowledge of the format of the data returned by the Proxy.<br>
4733  * <p>
4734  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4735  * instances from the data object. These records are cached and made available through accessor functions.
4736  * @constructor
4737  * Creates a new Store.
4738  * @param {Object} config A config object containing the objects needed for the Store to access data,
4739  * and read the data into Records.
4740  */
4741 Roo.data.Store = function(config){
4742     this.data = new Roo.util.MixedCollection(false);
4743     this.data.getKey = function(o){
4744         return o.id;
4745     };
4746     this.baseParams = {};
4747     // private
4748     this.paramNames = {
4749         "start" : "start",
4750         "limit" : "limit",
4751         "sort" : "sort",
4752         "dir" : "dir",
4753         "multisort" : "_multisort"
4754     };
4755
4756     if(config && config.data){
4757         this.inlineData = config.data;
4758         delete config.data;
4759     }
4760
4761     Roo.apply(this, config);
4762     
4763     if(this.reader){ // reader passed
4764         this.reader = Roo.factory(this.reader, Roo.data);
4765         this.reader.xmodule = this.xmodule || false;
4766         if(!this.recordType){
4767             this.recordType = this.reader.recordType;
4768         }
4769         if(this.reader.onMetaChange){
4770             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4771         }
4772     }
4773
4774     if(this.recordType){
4775         this.fields = this.recordType.prototype.fields;
4776     }
4777     this.modified = [];
4778
4779     this.addEvents({
4780         /**
4781          * @event datachanged
4782          * Fires when the data cache has changed, and a widget which is using this Store
4783          * as a Record cache should refresh its view.
4784          * @param {Store} this
4785          */
4786         datachanged : true,
4787         /**
4788          * @event metachange
4789          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4790          * @param {Store} this
4791          * @param {Object} meta The JSON metadata
4792          */
4793         metachange : true,
4794         /**
4795          * @event add
4796          * Fires when Records have been added to the Store
4797          * @param {Store} this
4798          * @param {Roo.data.Record[]} records The array of Records added
4799          * @param {Number} index The index at which the record(s) were added
4800          */
4801         add : true,
4802         /**
4803          * @event remove
4804          * Fires when a Record has been removed from the Store
4805          * @param {Store} this
4806          * @param {Roo.data.Record} record The Record that was removed
4807          * @param {Number} index The index at which the record was removed
4808          */
4809         remove : true,
4810         /**
4811          * @event update
4812          * Fires when a Record has been updated
4813          * @param {Store} this
4814          * @param {Roo.data.Record} record The Record that was updated
4815          * @param {String} operation The update operation being performed.  Value may be one of:
4816          * <pre><code>
4817  Roo.data.Record.EDIT
4818  Roo.data.Record.REJECT
4819  Roo.data.Record.COMMIT
4820          * </code></pre>
4821          */
4822         update : true,
4823         /**
4824          * @event clear
4825          * Fires when the data cache has been cleared.
4826          * @param {Store} this
4827          */
4828         clear : true,
4829         /**
4830          * @event beforeload
4831          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4832          * the load action will be canceled.
4833          * @param {Store} this
4834          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4835          */
4836         beforeload : true,
4837         /**
4838          * @event beforeloadadd
4839          * Fires after a new set of Records has been loaded.
4840          * @param {Store} this
4841          * @param {Roo.data.Record[]} records The Records that were loaded
4842          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4843          */
4844         beforeloadadd : true,
4845         /**
4846          * @event load
4847          * Fires after a new set of Records has been loaded, before they are added to the store.
4848          * @param {Store} this
4849          * @param {Roo.data.Record[]} records The Records that were loaded
4850          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4851          * @params {Object} return from reader
4852          */
4853         load : true,
4854         /**
4855          * @event loadexception
4856          * Fires if an exception occurs in the Proxy during loading.
4857          * Called with the signature of the Proxy's "loadexception" event.
4858          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4859          * 
4860          * @param {Proxy} 
4861          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4862          * @param {Object} load options 
4863          * @param {Object} jsonData from your request (normally this contains the Exception)
4864          */
4865         loadexception : true
4866     });
4867     
4868     if(this.proxy){
4869         this.proxy = Roo.factory(this.proxy, Roo.data);
4870         this.proxy.xmodule = this.xmodule || false;
4871         this.relayEvents(this.proxy,  ["loadexception"]);
4872     }
4873     this.sortToggle = {};
4874     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4875
4876     Roo.data.Store.superclass.constructor.call(this);
4877
4878     if(this.inlineData){
4879         this.loadData(this.inlineData);
4880         delete this.inlineData;
4881     }
4882 };
4883
4884 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4885      /**
4886     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4887     * without a remote query - used by combo/forms at present.
4888     */
4889     
4890     /**
4891     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4892     */
4893     /**
4894     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4895     */
4896     /**
4897     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4898     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4899     */
4900     /**
4901     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4902     * on any HTTP request
4903     */
4904     /**
4905     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4906     */
4907     /**
4908     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4909     */
4910     multiSort: false,
4911     /**
4912     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4913     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4914     */
4915     remoteSort : false,
4916
4917     /**
4918     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4919      * loaded or when a record is removed. (defaults to false).
4920     */
4921     pruneModifiedRecords : false,
4922
4923     // private
4924     lastOptions : null,
4925
4926     /**
4927      * Add Records to the Store and fires the add event.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     add : function(records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             records[i].join(this);
4934         }
4935         var index = this.data.length;
4936         this.data.addAll(records);
4937         this.fireEvent("add", this, records, index);
4938     },
4939
4940     /**
4941      * Remove a Record from the Store and fires the remove event.
4942      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4943      */
4944     remove : function(record){
4945         var index = this.data.indexOf(record);
4946         this.data.removeAt(index);
4947         if(this.pruneModifiedRecords){
4948             this.modified.remove(record);
4949         }
4950         this.fireEvent("remove", this, record, index);
4951     },
4952
4953     /**
4954      * Remove all Records from the Store and fires the clear event.
4955      */
4956     removeAll : function(){
4957         this.data.clear();
4958         if(this.pruneModifiedRecords){
4959             this.modified = [];
4960         }
4961         this.fireEvent("clear", this);
4962     },
4963
4964     /**
4965      * Inserts Records to the Store at the given index and fires the add event.
4966      * @param {Number} index The start index at which to insert the passed Records.
4967      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4968      */
4969     insert : function(index, records){
4970         records = [].concat(records);
4971         for(var i = 0, len = records.length; i < len; i++){
4972             this.data.insert(index, records[i]);
4973             records[i].join(this);
4974         }
4975         this.fireEvent("add", this, records, index);
4976     },
4977
4978     /**
4979      * Get the index within the cache of the passed Record.
4980      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4981      * @return {Number} The index of the passed Record. Returns -1 if not found.
4982      */
4983     indexOf : function(record){
4984         return this.data.indexOf(record);
4985     },
4986
4987     /**
4988      * Get the index within the cache of the Record with the passed id.
4989      * @param {String} id The id of the Record to find.
4990      * @return {Number} The index of the Record. Returns -1 if not found.
4991      */
4992     indexOfId : function(id){
4993         return this.data.indexOfKey(id);
4994     },
4995
4996     /**
4997      * Get the Record with the specified id.
4998      * @param {String} id The id of the Record to find.
4999      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
5000      */
5001     getById : function(id){
5002         return this.data.key(id);
5003     },
5004
5005     /**
5006      * Get the Record at the specified index.
5007      * @param {Number} index The index of the Record to find.
5008      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5009      */
5010     getAt : function(index){
5011         return this.data.itemAt(index);
5012     },
5013
5014     /**
5015      * Returns a range of Records between specified indices.
5016      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5017      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5018      * @return {Roo.data.Record[]} An array of Records
5019      */
5020     getRange : function(start, end){
5021         return this.data.getRange(start, end);
5022     },
5023
5024     // private
5025     storeOptions : function(o){
5026         o = Roo.apply({}, o);
5027         delete o.callback;
5028         delete o.scope;
5029         this.lastOptions = o;
5030     },
5031
5032     /**
5033      * Loads the Record cache from the configured Proxy using the configured Reader.
5034      * <p>
5035      * If using remote paging, then the first load call must specify the <em>start</em>
5036      * and <em>limit</em> properties in the options.params property to establish the initial
5037      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5038      * <p>
5039      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5040      * and this call will return before the new data has been loaded. Perform any post-processing
5041      * in a callback function, or in a "load" event handler.</strong>
5042      * <p>
5043      * @param {Object} options An object containing properties which control loading options:<ul>
5044      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5045      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5046      * passed the following arguments:<ul>
5047      * <li>r : Roo.data.Record[]</li>
5048      * <li>options: Options object from the load call</li>
5049      * <li>success: Boolean success indicator</li></ul></li>
5050      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5051      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5052      * </ul>
5053      */
5054     load : function(options){
5055         options = options || {};
5056         if(this.fireEvent("beforeload", this, options) !== false){
5057             this.storeOptions(options);
5058             var p = Roo.apply(options.params || {}, this.baseParams);
5059             // if meta was not loaded from remote source.. try requesting it.
5060             if (!this.reader.metaFromRemote) {
5061                 p._requestMeta = 1;
5062             }
5063             if(this.sortInfo && this.remoteSort){
5064                 var pn = this.paramNames;
5065                 p[pn["sort"]] = this.sortInfo.field;
5066                 p[pn["dir"]] = this.sortInfo.direction;
5067             }
5068             if (this.multiSort) {
5069                 var pn = this.paramNames;
5070                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5071             }
5072             
5073             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5074         }
5075     },
5076
5077     /**
5078      * Reloads the Record cache from the configured Proxy using the configured Reader and
5079      * the options from the last load operation performed.
5080      * @param {Object} options (optional) An object containing properties which may override the options
5081      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5082      * the most recently used options are reused).
5083      */
5084     reload : function(options){
5085         this.load(Roo.applyIf(options||{}, this.lastOptions));
5086     },
5087
5088     // private
5089     // Called as a callback by the Reader during a load operation.
5090     loadRecords : function(o, options, success){
5091         if(!o || success === false){
5092             if(success !== false){
5093                 this.fireEvent("load", this, [], options, o);
5094             }
5095             if(options.callback){
5096                 options.callback.call(options.scope || this, [], options, false);
5097             }
5098             return;
5099         }
5100         // if data returned failure - throw an exception.
5101         if (o.success === false) {
5102             // show a message if no listener is registered.
5103             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5104                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5105             }
5106             // loadmask wil be hooked into this..
5107             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5108             return;
5109         }
5110         var r = o.records, t = o.totalRecords || r.length;
5111         
5112         this.fireEvent("beforeloadadd", this, r, options, o);
5113         
5114         if(!options || options.add !== true){
5115             if(this.pruneModifiedRecords){
5116                 this.modified = [];
5117             }
5118             for(var i = 0, len = r.length; i < len; i++){
5119                 r[i].join(this);
5120             }
5121             if(this.snapshot){
5122                 this.data = this.snapshot;
5123                 delete this.snapshot;
5124             }
5125             this.data.clear();
5126             this.data.addAll(r);
5127             this.totalLength = t;
5128             this.applySort();
5129             this.fireEvent("datachanged", this);
5130         }else{
5131             this.totalLength = Math.max(t, this.data.length+r.length);
5132             this.add(r);
5133         }
5134         this.fireEvent("load", this, r, options, o);
5135         if(options.callback){
5136             options.callback.call(options.scope || this, r, options, true);
5137         }
5138     },
5139
5140
5141     /**
5142      * Loads data from a passed data block. A Reader which understands the format of the data
5143      * must have been configured in the constructor.
5144      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5145      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5146      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5147      */
5148     loadData : function(o, append){
5149         var r = this.reader.readRecords(o);
5150         this.loadRecords(r, {add: append}, true);
5151     },
5152
5153     /**
5154      * Gets the number of cached records.
5155      * <p>
5156      * <em>If using paging, this may not be the total size of the dataset. If the data object
5157      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5158      * the data set size</em>
5159      */
5160     getCount : function(){
5161         return this.data.length || 0;
5162     },
5163
5164     /**
5165      * Gets the total number of records in the dataset as returned by the server.
5166      * <p>
5167      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5168      * the dataset size</em>
5169      */
5170     getTotalCount : function(){
5171         return this.totalLength || 0;
5172     },
5173
5174     /**
5175      * Returns the sort state of the Store as an object with two properties:
5176      * <pre><code>
5177  field {String} The name of the field by which the Records are sorted
5178  direction {String} The sort order, "ASC" or "DESC"
5179      * </code></pre>
5180      */
5181     getSortState : function(){
5182         return this.sortInfo;
5183     },
5184
5185     // private
5186     applySort : function(){
5187         if(this.sortInfo && !this.remoteSort){
5188             var s = this.sortInfo, f = s.field;
5189             var st = this.fields.get(f).sortType;
5190             var fn = function(r1, r2){
5191                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5192                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5193             };
5194             this.data.sort(s.direction, fn);
5195             if(this.snapshot && this.snapshot != this.data){
5196                 this.snapshot.sort(s.direction, fn);
5197             }
5198         }
5199     },
5200
5201     /**
5202      * Sets the default sort column and order to be used by the next load operation.
5203      * @param {String} fieldName The name of the field to sort by.
5204      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5205      */
5206     setDefaultSort : function(field, dir){
5207         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5208     },
5209
5210     /**
5211      * Sort the Records.
5212      * If remote sorting is used, the sort is performed on the server, and the cache is
5213      * reloaded. If local sorting is used, the cache is sorted internally.
5214      * @param {String} fieldName The name of the field to sort by.
5215      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5216      */
5217     sort : function(fieldName, dir){
5218         var f = this.fields.get(fieldName);
5219         if(!dir){
5220             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5221             
5222             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5223                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5224             }else{
5225                 dir = f.sortDir;
5226             }
5227         }
5228         this.sortToggle[f.name] = dir;
5229         this.sortInfo = {field: f.name, direction: dir};
5230         if(!this.remoteSort){
5231             this.applySort();
5232             this.fireEvent("datachanged", this);
5233         }else{
5234             this.load(this.lastOptions);
5235         }
5236     },
5237
5238     /**
5239      * Calls the specified function for each of the Records in the cache.
5240      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5241      * Returning <em>false</em> aborts and exits the iteration.
5242      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5243      */
5244     each : function(fn, scope){
5245         this.data.each(fn, scope);
5246     },
5247
5248     /**
5249      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5250      * (e.g., during paging).
5251      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5252      */
5253     getModifiedRecords : function(){
5254         return this.modified;
5255     },
5256
5257     // private
5258     createFilterFn : function(property, value, anyMatch){
5259         if(!value.exec){ // not a regex
5260             value = String(value);
5261             if(value.length == 0){
5262                 return false;
5263             }
5264             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5265         }
5266         return function(r){
5267             return value.test(r.data[property]);
5268         };
5269     },
5270
5271     /**
5272      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5273      * @param {String} property A field on your records
5274      * @param {Number} start The record index to start at (defaults to 0)
5275      * @param {Number} end The last record index to include (defaults to length - 1)
5276      * @return {Number} The sum
5277      */
5278     sum : function(property, start, end){
5279         var rs = this.data.items, v = 0;
5280         start = start || 0;
5281         end = (end || end === 0) ? end : rs.length-1;
5282
5283         for(var i = start; i <= end; i++){
5284             v += (rs[i].data[property] || 0);
5285         }
5286         return v;
5287     },
5288
5289     /**
5290      * Filter the records by a specified property.
5291      * @param {String} field A field on your records
5292      * @param {String/RegExp} value Either a string that the field
5293      * should start with or a RegExp to test against the field
5294      * @param {Boolean} anyMatch True to match any part not just the beginning
5295      */
5296     filter : function(property, value, anyMatch){
5297         var fn = this.createFilterFn(property, value, anyMatch);
5298         return fn ? this.filterBy(fn) : this.clearFilter();
5299     },
5300
5301     /**
5302      * Filter by a function. The specified function will be called with each
5303      * record in this data source. If the function returns true the record is included,
5304      * otherwise it is filtered.
5305      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5306      * @param {Object} scope (optional) The scope of the function (defaults to this)
5307      */
5308     filterBy : function(fn, scope){
5309         this.snapshot = this.snapshot || this.data;
5310         this.data = this.queryBy(fn, scope||this);
5311         this.fireEvent("datachanged", this);
5312     },
5313
5314     /**
5315      * Query the records by a specified property.
5316      * @param {String} field A field on your records
5317      * @param {String/RegExp} value Either a string that the field
5318      * should start with or a RegExp to test against the field
5319      * @param {Boolean} anyMatch True to match any part not just the beginning
5320      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5321      */
5322     query : function(property, value, anyMatch){
5323         var fn = this.createFilterFn(property, value, anyMatch);
5324         return fn ? this.queryBy(fn) : this.data.clone();
5325     },
5326
5327     /**
5328      * Query by a function. The specified function will be called with each
5329      * record in this data source. If the function returns true the record is included
5330      * in the results.
5331      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5332      * @param {Object} scope (optional) The scope of the function (defaults to this)
5333       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5334      **/
5335     queryBy : function(fn, scope){
5336         var data = this.snapshot || this.data;
5337         return data.filterBy(fn, scope||this);
5338     },
5339
5340     /**
5341      * Collects unique values for a particular dataIndex from this store.
5342      * @param {String} dataIndex The property to collect
5343      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5344      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5345      * @return {Array} An array of the unique values
5346      **/
5347     collect : function(dataIndex, allowNull, bypassFilter){
5348         var d = (bypassFilter === true && this.snapshot) ?
5349                 this.snapshot.items : this.data.items;
5350         var v, sv, r = [], l = {};
5351         for(var i = 0, len = d.length; i < len; i++){
5352             v = d[i].data[dataIndex];
5353             sv = String(v);
5354             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5355                 l[sv] = true;
5356                 r[r.length] = v;
5357             }
5358         }
5359         return r;
5360     },
5361
5362     /**
5363      * Revert to a view of the Record cache with no filtering applied.
5364      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5365      */
5366     clearFilter : function(suppressEvent){
5367         if(this.snapshot && this.snapshot != this.data){
5368             this.data = this.snapshot;
5369             delete this.snapshot;
5370             if(suppressEvent !== true){
5371                 this.fireEvent("datachanged", this);
5372             }
5373         }
5374     },
5375
5376     // private
5377     afterEdit : function(record){
5378         if(this.modified.indexOf(record) == -1){
5379             this.modified.push(record);
5380         }
5381         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5382     },
5383     
5384     // private
5385     afterReject : function(record){
5386         this.modified.remove(record);
5387         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5388     },
5389
5390     // private
5391     afterCommit : function(record){
5392         this.modified.remove(record);
5393         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5394     },
5395
5396     /**
5397      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5398      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5399      */
5400     commitChanges : function(){
5401         var m = this.modified.slice(0);
5402         this.modified = [];
5403         for(var i = 0, len = m.length; i < len; i++){
5404             m[i].commit();
5405         }
5406     },
5407
5408     /**
5409      * Cancel outstanding changes on all changed records.
5410      */
5411     rejectChanges : function(){
5412         var m = this.modified.slice(0);
5413         this.modified = [];
5414         for(var i = 0, len = m.length; i < len; i++){
5415             m[i].reject();
5416         }
5417     },
5418
5419     onMetaChange : function(meta, rtype, o){
5420         this.recordType = rtype;
5421         this.fields = rtype.prototype.fields;
5422         delete this.snapshot;
5423         this.sortInfo = meta.sortInfo || this.sortInfo;
5424         this.modified = [];
5425         this.fireEvent('metachange', this, this.reader.meta);
5426     },
5427     
5428     moveIndex : function(data, type)
5429     {
5430         var index = this.indexOf(data);
5431         
5432         var newIndex = index + type;
5433         
5434         this.remove(data);
5435         
5436         this.insert(newIndex, data);
5437         
5438     }
5439 });/*
5440  * Based on:
5441  * Ext JS Library 1.1.1
5442  * Copyright(c) 2006-2007, Ext JS, LLC.
5443  *
5444  * Originally Released Under LGPL - original licence link has changed is not relivant.
5445  *
5446  * Fork - LGPL
5447  * <script type="text/javascript">
5448  */
5449
5450 /**
5451  * @class Roo.data.SimpleStore
5452  * @extends Roo.data.Store
5453  * Small helper class to make creating Stores from Array data easier.
5454  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5455  * @cfg {Array} fields An array of field definition objects, or field name strings.
5456  * @cfg {Array} data The multi-dimensional array of data
5457  * @constructor
5458  * @param {Object} config
5459  */
5460 Roo.data.SimpleStore = function(config){
5461     Roo.data.SimpleStore.superclass.constructor.call(this, {
5462         isLocal : true,
5463         reader: new Roo.data.ArrayReader({
5464                 id: config.id
5465             },
5466             Roo.data.Record.create(config.fields)
5467         ),
5468         proxy : new Roo.data.MemoryProxy(config.data)
5469     });
5470     this.load();
5471 };
5472 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5473  * Based on:
5474  * Ext JS Library 1.1.1
5475  * Copyright(c) 2006-2007, Ext JS, LLC.
5476  *
5477  * Originally Released Under LGPL - original licence link has changed is not relivant.
5478  *
5479  * Fork - LGPL
5480  * <script type="text/javascript">
5481  */
5482
5483 /**
5484 /**
5485  * @extends Roo.data.Store
5486  * @class Roo.data.JsonStore
5487  * Small helper class to make creating Stores for JSON data easier. <br/>
5488 <pre><code>
5489 var store = new Roo.data.JsonStore({
5490     url: 'get-images.php',
5491     root: 'images',
5492     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5493 });
5494 </code></pre>
5495  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5496  * JsonReader and HttpProxy (unless inline data is provided).</b>
5497  * @cfg {Array} fields An array of field definition objects, or field name strings.
5498  * @constructor
5499  * @param {Object} config
5500  */
5501 Roo.data.JsonStore = function(c){
5502     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5503         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5504         reader: new Roo.data.JsonReader(c, c.fields)
5505     }));
5506 };
5507 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5508  * Based on:
5509  * Ext JS Library 1.1.1
5510  * Copyright(c) 2006-2007, Ext JS, LLC.
5511  *
5512  * Originally Released Under LGPL - original licence link has changed is not relivant.
5513  *
5514  * Fork - LGPL
5515  * <script type="text/javascript">
5516  */
5517
5518  
5519 Roo.data.Field = function(config){
5520     if(typeof config == "string"){
5521         config = {name: config};
5522     }
5523     Roo.apply(this, config);
5524     
5525     if(!this.type){
5526         this.type = "auto";
5527     }
5528     
5529     var st = Roo.data.SortTypes;
5530     // named sortTypes are supported, here we look them up
5531     if(typeof this.sortType == "string"){
5532         this.sortType = st[this.sortType];
5533     }
5534     
5535     // set default sortType for strings and dates
5536     if(!this.sortType){
5537         switch(this.type){
5538             case "string":
5539                 this.sortType = st.asUCString;
5540                 break;
5541             case "date":
5542                 this.sortType = st.asDate;
5543                 break;
5544             default:
5545                 this.sortType = st.none;
5546         }
5547     }
5548
5549     // define once
5550     var stripRe = /[\$,%]/g;
5551
5552     // prebuilt conversion function for this field, instead of
5553     // switching every time we're reading a value
5554     if(!this.convert){
5555         var cv, dateFormat = this.dateFormat;
5556         switch(this.type){
5557             case "":
5558             case "auto":
5559             case undefined:
5560                 cv = function(v){ return v; };
5561                 break;
5562             case "string":
5563                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5564                 break;
5565             case "int":
5566                 cv = function(v){
5567                     return v !== undefined && v !== null && v !== '' ?
5568                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5569                     };
5570                 break;
5571             case "float":
5572                 cv = function(v){
5573                     return v !== undefined && v !== null && v !== '' ?
5574                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5575                     };
5576                 break;
5577             case "bool":
5578             case "boolean":
5579                 cv = function(v){ return v === true || v === "true" || v == 1; };
5580                 break;
5581             case "date":
5582                 cv = function(v){
5583                     if(!v){
5584                         return '';
5585                     }
5586                     if(v instanceof Date){
5587                         return v;
5588                     }
5589                     if(dateFormat){
5590                         if(dateFormat == "timestamp"){
5591                             return new Date(v*1000);
5592                         }
5593                         return Date.parseDate(v, dateFormat);
5594                     }
5595                     var parsed = Date.parse(v);
5596                     return parsed ? new Date(parsed) : null;
5597                 };
5598              break;
5599             
5600         }
5601         this.convert = cv;
5602     }
5603 };
5604
5605 Roo.data.Field.prototype = {
5606     dateFormat: null,
5607     defaultValue: "",
5608     mapping: null,
5609     sortType : null,
5610     sortDir : "ASC"
5611 };/*
5612  * Based on:
5613  * Ext JS Library 1.1.1
5614  * Copyright(c) 2006-2007, Ext JS, LLC.
5615  *
5616  * Originally Released Under LGPL - original licence link has changed is not relivant.
5617  *
5618  * Fork - LGPL
5619  * <script type="text/javascript">
5620  */
5621  
5622 // Base class for reading structured data from a data source.  This class is intended to be
5623 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5624
5625 /**
5626  * @class Roo.data.DataReader
5627  * Base class for reading structured data from a data source.  This class is intended to be
5628  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5629  */
5630
5631 Roo.data.DataReader = function(meta, recordType){
5632     
5633     this.meta = meta;
5634     
5635     this.recordType = recordType instanceof Array ? 
5636         Roo.data.Record.create(recordType) : recordType;
5637 };
5638
5639 Roo.data.DataReader.prototype = {
5640      /**
5641      * Create an empty record
5642      * @param {Object} data (optional) - overlay some values
5643      * @return {Roo.data.Record} record created.
5644      */
5645     newRow :  function(d) {
5646         var da =  {};
5647         this.recordType.prototype.fields.each(function(c) {
5648             switch( c.type) {
5649                 case 'int' : da[c.name] = 0; break;
5650                 case 'date' : da[c.name] = new Date(); break;
5651                 case 'float' : da[c.name] = 0.0; break;
5652                 case 'boolean' : da[c.name] = false; break;
5653                 default : da[c.name] = ""; break;
5654             }
5655             
5656         });
5657         return new this.recordType(Roo.apply(da, d));
5658     }
5659     
5660 };/*
5661  * Based on:
5662  * Ext JS Library 1.1.1
5663  * Copyright(c) 2006-2007, Ext JS, LLC.
5664  *
5665  * Originally Released Under LGPL - original licence link has changed is not relivant.
5666  *
5667  * Fork - LGPL
5668  * <script type="text/javascript">
5669  */
5670
5671 /**
5672  * @class Roo.data.DataProxy
5673  * @extends Roo.data.Observable
5674  * This class is an abstract base class for implementations which provide retrieval of
5675  * unformatted data objects.<br>
5676  * <p>
5677  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5678  * (of the appropriate type which knows how to parse the data object) to provide a block of
5679  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5680  * <p>
5681  * Custom implementations must implement the load method as described in
5682  * {@link Roo.data.HttpProxy#load}.
5683  */
5684 Roo.data.DataProxy = function(){
5685     this.addEvents({
5686         /**
5687          * @event beforeload
5688          * Fires before a network request is made to retrieve a data object.
5689          * @param {Object} This DataProxy object.
5690          * @param {Object} params The params parameter to the load function.
5691          */
5692         beforeload : true,
5693         /**
5694          * @event load
5695          * Fires before the load method's callback is called.
5696          * @param {Object} This DataProxy object.
5697          * @param {Object} o The data object.
5698          * @param {Object} arg The callback argument object passed to the load function.
5699          */
5700         load : true,
5701         /**
5702          * @event loadexception
5703          * Fires if an Exception occurs during data retrieval.
5704          * @param {Object} This DataProxy object.
5705          * @param {Object} o The data object.
5706          * @param {Object} arg The callback argument object passed to the load function.
5707          * @param {Object} e The Exception.
5708          */
5709         loadexception : true
5710     });
5711     Roo.data.DataProxy.superclass.constructor.call(this);
5712 };
5713
5714 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5715
5716     /**
5717      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5718      */
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.MemoryProxy
5731  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5732  * to the Reader when its load method is called.
5733  * @constructor
5734  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5735  */
5736 Roo.data.MemoryProxy = function(data){
5737     if (data.data) {
5738         data = data.data;
5739     }
5740     Roo.data.MemoryProxy.superclass.constructor.call(this);
5741     this.data = data;
5742 };
5743
5744 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5745     /**
5746      * Load data from the requested source (in this case an in-memory
5747      * data object passed to the constructor), read the data object into
5748      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5749      * process that block using the passed callback.
5750      * @param {Object} params This parameter is not used by the MemoryProxy class.
5751      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5752      * object into a block of Roo.data.Records.
5753      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5754      * The function must be passed <ul>
5755      * <li>The Record block object</li>
5756      * <li>The "arg" argument from the load function</li>
5757      * <li>A boolean success indicator</li>
5758      * </ul>
5759      * @param {Object} scope The scope in which to call the callback
5760      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5761      */
5762     load : function(params, reader, callback, scope, arg){
5763         params = params || {};
5764         var result;
5765         try {
5766             result = reader.readRecords(this.data);
5767         }catch(e){
5768             this.fireEvent("loadexception", this, arg, null, e);
5769             callback.call(scope, null, arg, false);
5770             return;
5771         }
5772         callback.call(scope, result, arg, true);
5773     },
5774     
5775     // private
5776     update : function(params, records){
5777         
5778     }
5779 });/*
5780  * Based on:
5781  * Ext JS Library 1.1.1
5782  * Copyright(c) 2006-2007, Ext JS, LLC.
5783  *
5784  * Originally Released Under LGPL - original licence link has changed is not relivant.
5785  *
5786  * Fork - LGPL
5787  * <script type="text/javascript">
5788  */
5789 /**
5790  * @class Roo.data.HttpProxy
5791  * @extends Roo.data.DataProxy
5792  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5793  * configured to reference a certain URL.<br><br>
5794  * <p>
5795  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5796  * from which the running page was served.<br><br>
5797  * <p>
5798  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5799  * <p>
5800  * Be aware that to enable the browser to parse an XML document, the server must set
5801  * the Content-Type header in the HTTP response to "text/xml".
5802  * @constructor
5803  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5804  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5805  * will be used to make the request.
5806  */
5807 Roo.data.HttpProxy = function(conn){
5808     Roo.data.HttpProxy.superclass.constructor.call(this);
5809     // is conn a conn config or a real conn?
5810     this.conn = conn;
5811     this.useAjax = !conn || !conn.events;
5812   
5813 };
5814
5815 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5816     // thse are take from connection...
5817     
5818     /**
5819      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5823      * extra parameters to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5827      *  to each request made by this object. (defaults to undefined)
5828      */
5829     /**
5830      * @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)
5831      */
5832     /**
5833      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5834      */
5835      /**
5836      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5837      * @type Boolean
5838      */
5839   
5840
5841     /**
5842      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5843      * @type Boolean
5844      */
5845     /**
5846      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5847      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5848      * a finer-grained basis than the DataProxy events.
5849      */
5850     getConnection : function(){
5851         return this.useAjax ? Roo.Ajax : this.conn;
5852     },
5853
5854     /**
5855      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5856      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5857      * process that block using the passed callback.
5858      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5859      * for the request to the remote server.
5860      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5861      * object into a block of Roo.data.Records.
5862      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5863      * The function must be passed <ul>
5864      * <li>The Record block object</li>
5865      * <li>The "arg" argument from the load function</li>
5866      * <li>A boolean success indicator</li>
5867      * </ul>
5868      * @param {Object} scope The scope in which to call the callback
5869      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5870      */
5871     load : function(params, reader, callback, scope, arg){
5872         if(this.fireEvent("beforeload", this, params) !== false){
5873             var  o = {
5874                 params : params || {},
5875                 request: {
5876                     callback : callback,
5877                     scope : scope,
5878                     arg : arg
5879                 },
5880                 reader: reader,
5881                 callback : this.loadResponse,
5882                 scope: this
5883             };
5884             if(this.useAjax){
5885                 Roo.applyIf(o, this.conn);
5886                 if(this.activeRequest){
5887                     Roo.Ajax.abort(this.activeRequest);
5888                 }
5889                 this.activeRequest = Roo.Ajax.request(o);
5890             }else{
5891                 this.conn.request(o);
5892             }
5893         }else{
5894             callback.call(scope||this, null, arg, false);
5895         }
5896     },
5897
5898     // private
5899     loadResponse : function(o, success, response){
5900         delete this.activeRequest;
5901         if(!success){
5902             this.fireEvent("loadexception", this, o, response);
5903             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5904             return;
5905         }
5906         var result;
5907         try {
5908             result = o.reader.read(response);
5909         }catch(e){
5910             this.fireEvent("loadexception", this, o, response, e);
5911             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5912             return;
5913         }
5914         
5915         this.fireEvent("load", this, o, o.request.arg);
5916         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5917     },
5918
5919     // private
5920     update : function(dataSet){
5921
5922     },
5923
5924     // private
5925     updateResponse : function(dataSet){
5926
5927     }
5928 });/*
5929  * Based on:
5930  * Ext JS Library 1.1.1
5931  * Copyright(c) 2006-2007, Ext JS, LLC.
5932  *
5933  * Originally Released Under LGPL - original licence link has changed is not relivant.
5934  *
5935  * Fork - LGPL
5936  * <script type="text/javascript">
5937  */
5938
5939 /**
5940  * @class Roo.data.ScriptTagProxy
5941  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5942  * other than the originating domain of the running page.<br><br>
5943  * <p>
5944  * <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
5945  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5946  * <p>
5947  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5948  * source code that is used as the source inside a &lt;script> tag.<br><br>
5949  * <p>
5950  * In order for the browser to process the returned data, the server must wrap the data object
5951  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5952  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5953  * depending on whether the callback name was passed:
5954  * <p>
5955  * <pre><code>
5956 boolean scriptTag = false;
5957 String cb = request.getParameter("callback");
5958 if (cb != null) {
5959     scriptTag = true;
5960     response.setContentType("text/javascript");
5961 } else {
5962     response.setContentType("application/x-json");
5963 }
5964 Writer out = response.getWriter();
5965 if (scriptTag) {
5966     out.write(cb + "(");
5967 }
5968 out.print(dataBlock.toJsonString());
5969 if (scriptTag) {
5970     out.write(");");
5971 }
5972 </pre></code>
5973  *
5974  * @constructor
5975  * @param {Object} config A configuration object.
5976  */
5977 Roo.data.ScriptTagProxy = function(config){
5978     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5979     Roo.apply(this, config);
5980     this.head = document.getElementsByTagName("head")[0];
5981 };
5982
5983 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5984
5985 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5986     /**
5987      * @cfg {String} url The URL from which to request the data object.
5988      */
5989     /**
5990      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5991      */
5992     timeout : 30000,
5993     /**
5994      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5995      * the server the name of the callback function set up by the load call to process the returned data object.
5996      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5997      * javascript output which calls this named function passing the data object as its only parameter.
5998      */
5999     callbackParam : "callback",
6000     /**
6001      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
6002      * name to the request.
6003      */
6004     nocache : true,
6005
6006     /**
6007      * Load data from the configured URL, read the data object into
6008      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6009      * process that block using the passed callback.
6010      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6011      * for the request to the remote server.
6012      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6013      * object into a block of Roo.data.Records.
6014      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6015      * The function must be passed <ul>
6016      * <li>The Record block object</li>
6017      * <li>The "arg" argument from the load function</li>
6018      * <li>A boolean success indicator</li>
6019      * </ul>
6020      * @param {Object} scope The scope in which to call the callback
6021      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6022      */
6023     load : function(params, reader, callback, scope, arg){
6024         if(this.fireEvent("beforeload", this, params) !== false){
6025
6026             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6027
6028             var url = this.url;
6029             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6030             if(this.nocache){
6031                 url += "&_dc=" + (new Date().getTime());
6032             }
6033             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6034             var trans = {
6035                 id : transId,
6036                 cb : "stcCallback"+transId,
6037                 scriptId : "stcScript"+transId,
6038                 params : params,
6039                 arg : arg,
6040                 url : url,
6041                 callback : callback,
6042                 scope : scope,
6043                 reader : reader
6044             };
6045             var conn = this;
6046
6047             window[trans.cb] = function(o){
6048                 conn.handleResponse(o, trans);
6049             };
6050
6051             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6052
6053             if(this.autoAbort !== false){
6054                 this.abort();
6055             }
6056
6057             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6058
6059             var script = document.createElement("script");
6060             script.setAttribute("src", url);
6061             script.setAttribute("type", "text/javascript");
6062             script.setAttribute("id", trans.scriptId);
6063             this.head.appendChild(script);
6064
6065             this.trans = trans;
6066         }else{
6067             callback.call(scope||this, null, arg, false);
6068         }
6069     },
6070
6071     // private
6072     isLoading : function(){
6073         return this.trans ? true : false;
6074     },
6075
6076     /**
6077      * Abort the current server request.
6078      */
6079     abort : function(){
6080         if(this.isLoading()){
6081             this.destroyTrans(this.trans);
6082         }
6083     },
6084
6085     // private
6086     destroyTrans : function(trans, isLoaded){
6087         this.head.removeChild(document.getElementById(trans.scriptId));
6088         clearTimeout(trans.timeoutId);
6089         if(isLoaded){
6090             window[trans.cb] = undefined;
6091             try{
6092                 delete window[trans.cb];
6093             }catch(e){}
6094         }else{
6095             // if hasn't been loaded, wait for load to remove it to prevent script error
6096             window[trans.cb] = function(){
6097                 window[trans.cb] = undefined;
6098                 try{
6099                     delete window[trans.cb];
6100                 }catch(e){}
6101             };
6102         }
6103     },
6104
6105     // private
6106     handleResponse : function(o, trans){
6107         this.trans = false;
6108         this.destroyTrans(trans, true);
6109         var result;
6110         try {
6111             result = trans.reader.readRecords(o);
6112         }catch(e){
6113             this.fireEvent("loadexception", this, o, trans.arg, e);
6114             trans.callback.call(trans.scope||window, null, trans.arg, false);
6115             return;
6116         }
6117         this.fireEvent("load", this, o, trans.arg);
6118         trans.callback.call(trans.scope||window, result, trans.arg, true);
6119     },
6120
6121     // private
6122     handleFailure : function(trans){
6123         this.trans = false;
6124         this.destroyTrans(trans, false);
6125         this.fireEvent("loadexception", this, null, trans.arg);
6126         trans.callback.call(trans.scope||window, null, trans.arg, false);
6127     }
6128 });/*
6129  * Based on:
6130  * Ext JS Library 1.1.1
6131  * Copyright(c) 2006-2007, Ext JS, LLC.
6132  *
6133  * Originally Released Under LGPL - original licence link has changed is not relivant.
6134  *
6135  * Fork - LGPL
6136  * <script type="text/javascript">
6137  */
6138
6139 /**
6140  * @class Roo.data.JsonReader
6141  * @extends Roo.data.DataReader
6142  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6143  * based on mappings in a provided Roo.data.Record constructor.
6144  * 
6145  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6146  * in the reply previously. 
6147  * 
6148  * <p>
6149  * Example code:
6150  * <pre><code>
6151 var RecordDef = Roo.data.Record.create([
6152     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6153     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6154 ]);
6155 var myReader = new Roo.data.JsonReader({
6156     totalProperty: "results",    // The property which contains the total dataset size (optional)
6157     root: "rows",                // The property which contains an Array of row objects
6158     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6159 }, RecordDef);
6160 </code></pre>
6161  * <p>
6162  * This would consume a JSON file like this:
6163  * <pre><code>
6164 { 'results': 2, 'rows': [
6165     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6166     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6167 }
6168 </code></pre>
6169  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6170  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6171  * paged from the remote server.
6172  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6173  * @cfg {String} root name of the property which contains the Array of row objects.
6174  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6175  * @cfg {Array} fields Array of field definition objects
6176  * @constructor
6177  * Create a new JsonReader
6178  * @param {Object} meta Metadata configuration options
6179  * @param {Object} recordType Either an Array of field definition objects,
6180  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6181  */
6182 Roo.data.JsonReader = function(meta, recordType){
6183     
6184     meta = meta || {};
6185     // set some defaults:
6186     Roo.applyIf(meta, {
6187         totalProperty: 'total',
6188         successProperty : 'success',
6189         root : 'data',
6190         id : 'id'
6191     });
6192     
6193     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6194 };
6195 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6196     
6197     /**
6198      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6199      * Used by Store query builder to append _requestMeta to params.
6200      * 
6201      */
6202     metaFromRemote : false,
6203     /**
6204      * This method is only used by a DataProxy which has retrieved data from a remote server.
6205      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6206      * @return {Object} data A data block which is used by an Roo.data.Store object as
6207      * a cache of Roo.data.Records.
6208      */
6209     read : function(response){
6210         var json = response.responseText;
6211        
6212         var o = /* eval:var:o */ eval("("+json+")");
6213         if(!o) {
6214             throw {message: "JsonReader.read: Json object not found"};
6215         }
6216         
6217         if(o.metaData){
6218             
6219             delete this.ef;
6220             this.metaFromRemote = true;
6221             this.meta = o.metaData;
6222             this.recordType = Roo.data.Record.create(o.metaData.fields);
6223             this.onMetaChange(this.meta, this.recordType, o);
6224         }
6225         return this.readRecords(o);
6226     },
6227
6228     // private function a store will implement
6229     onMetaChange : function(meta, recordType, o){
6230
6231     },
6232
6233     /**
6234          * @ignore
6235          */
6236     simpleAccess: function(obj, subsc) {
6237         return obj[subsc];
6238     },
6239
6240         /**
6241          * @ignore
6242          */
6243     getJsonAccessor: function(){
6244         var re = /[\[\.]/;
6245         return function(expr) {
6246             try {
6247                 return(re.test(expr))
6248                     ? new Function("obj", "return obj." + expr)
6249                     : function(obj){
6250                         return obj[expr];
6251                     };
6252             } catch(e){}
6253             return Roo.emptyFn;
6254         };
6255     }(),
6256
6257     /**
6258      * Create a data block containing Roo.data.Records from an XML document.
6259      * @param {Object} o An object which contains an Array of row objects in the property specified
6260      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6261      * which contains the total size of the dataset.
6262      * @return {Object} data A data block which is used by an Roo.data.Store object as
6263      * a cache of Roo.data.Records.
6264      */
6265     readRecords : function(o){
6266         /**
6267          * After any data loads, the raw JSON data is available for further custom processing.
6268          * @type Object
6269          */
6270         this.o = o;
6271         var s = this.meta, Record = this.recordType,
6272             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6273
6274 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6275         if (!this.ef) {
6276             if(s.totalProperty) {
6277                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6278                 }
6279                 if(s.successProperty) {
6280                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6281                 }
6282                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6283                 if (s.id) {
6284                         var g = this.getJsonAccessor(s.id);
6285                         this.getId = function(rec) {
6286                                 var r = g(rec);  
6287                                 return (r === undefined || r === "") ? null : r;
6288                         };
6289                 } else {
6290                         this.getId = function(){return null;};
6291                 }
6292             this.ef = [];
6293             for(var jj = 0; jj < fl; jj++){
6294                 f = fi[jj];
6295                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6296                 this.ef[jj] = this.getJsonAccessor(map);
6297             }
6298         }
6299
6300         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6301         if(s.totalProperty){
6302             var vt = parseInt(this.getTotal(o), 10);
6303             if(!isNaN(vt)){
6304                 totalRecords = vt;
6305             }
6306         }
6307         if(s.successProperty){
6308             var vs = this.getSuccess(o);
6309             if(vs === false || vs === 'false'){
6310                 success = false;
6311             }
6312         }
6313         var records = [];
6314         for(var i = 0; i < c; i++){
6315                 var n = root[i];
6316             var values = {};
6317             var id = this.getId(n);
6318             for(var j = 0; j < fl; j++){
6319                 f = fi[j];
6320             var v = this.ef[j](n);
6321             if (!f.convert) {
6322                 Roo.log('missing convert for ' + f.name);
6323                 Roo.log(f);
6324                 continue;
6325             }
6326             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6327             }
6328             var record = new Record(values, id);
6329             record.json = n;
6330             records[i] = record;
6331         }
6332         return {
6333             raw : o,
6334             success : success,
6335             records : records,
6336             totalRecords : totalRecords
6337         };
6338     }
6339 });/*
6340  * Based on:
6341  * Ext JS Library 1.1.1
6342  * Copyright(c) 2006-2007, Ext JS, LLC.
6343  *
6344  * Originally Released Under LGPL - original licence link has changed is not relivant.
6345  *
6346  * Fork - LGPL
6347  * <script type="text/javascript">
6348  */
6349
6350 /**
6351  * @class Roo.data.XmlReader
6352  * @extends Roo.data.DataReader
6353  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6354  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6355  * <p>
6356  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6357  * header in the HTTP response must be set to "text/xml".</em>
6358  * <p>
6359  * Example code:
6360  * <pre><code>
6361 var RecordDef = Roo.data.Record.create([
6362    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6363    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6364 ]);
6365 var myReader = new Roo.data.XmlReader({
6366    totalRecords: "results", // The element which contains the total dataset size (optional)
6367    record: "row",           // The repeated element which contains row information
6368    id: "id"                 // The element within the row that provides an ID for the record (optional)
6369 }, RecordDef);
6370 </code></pre>
6371  * <p>
6372  * This would consume an XML file like this:
6373  * <pre><code>
6374 &lt;?xml?>
6375 &lt;dataset>
6376  &lt;results>2&lt;/results>
6377  &lt;row>
6378    &lt;id>1&lt;/id>
6379    &lt;name>Bill&lt;/name>
6380    &lt;occupation>Gardener&lt;/occupation>
6381  &lt;/row>
6382  &lt;row>
6383    &lt;id>2&lt;/id>
6384    &lt;name>Ben&lt;/name>
6385    &lt;occupation>Horticulturalist&lt;/occupation>
6386  &lt;/row>
6387 &lt;/dataset>
6388 </code></pre>
6389  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6390  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6391  * paged from the remote server.
6392  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6393  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6394  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6395  * a record identifier value.
6396  * @constructor
6397  * Create a new XmlReader
6398  * @param {Object} meta Metadata configuration options
6399  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6400  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6401  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6402  */
6403 Roo.data.XmlReader = function(meta, recordType){
6404     meta = meta || {};
6405     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6406 };
6407 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6408     /**
6409      * This method is only used by a DataProxy which has retrieved data from a remote server.
6410          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6411          * to contain a method called 'responseXML' that returns an XML document object.
6412      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6413      * a cache of Roo.data.Records.
6414      */
6415     read : function(response){
6416         var doc = response.responseXML;
6417         if(!doc) {
6418             throw {message: "XmlReader.read: XML Document not available"};
6419         }
6420         return this.readRecords(doc);
6421     },
6422
6423     /**
6424      * Create a data block containing Roo.data.Records from an XML document.
6425          * @param {Object} doc A parsed XML document.
6426      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6427      * a cache of Roo.data.Records.
6428      */
6429     readRecords : function(doc){
6430         /**
6431          * After any data loads/reads, the raw XML Document is available for further custom processing.
6432          * @type XMLDocument
6433          */
6434         this.xmlData = doc;
6435         var root = doc.documentElement || doc;
6436         var q = Roo.DomQuery;
6437         var recordType = this.recordType, fields = recordType.prototype.fields;
6438         var sid = this.meta.id;
6439         var totalRecords = 0, success = true;
6440         if(this.meta.totalRecords){
6441             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6442         }
6443         
6444         if(this.meta.success){
6445             var sv = q.selectValue(this.meta.success, root, true);
6446             success = sv !== false && sv !== 'false';
6447         }
6448         var records = [];
6449         var ns = q.select(this.meta.record, root);
6450         for(var i = 0, len = ns.length; i < len; i++) {
6451                 var n = ns[i];
6452                 var values = {};
6453                 var id = sid ? q.selectValue(sid, n) : undefined;
6454                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6455                     var f = fields.items[j];
6456                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6457                     v = f.convert(v);
6458                     values[f.name] = v;
6459                 }
6460                 var record = new recordType(values, id);
6461                 record.node = n;
6462                 records[records.length] = record;
6463             }
6464
6465             return {
6466                 success : success,
6467                 records : records,
6468                 totalRecords : totalRecords || records.length
6469             };
6470     }
6471 });/*
6472  * Based on:
6473  * Ext JS Library 1.1.1
6474  * Copyright(c) 2006-2007, Ext JS, LLC.
6475  *
6476  * Originally Released Under LGPL - original licence link has changed is not relivant.
6477  *
6478  * Fork - LGPL
6479  * <script type="text/javascript">
6480  */
6481
6482 /**
6483  * @class Roo.data.ArrayReader
6484  * @extends Roo.data.DataReader
6485  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6486  * Each element of that Array represents a row of data fields. The
6487  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6488  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6489  * <p>
6490  * Example code:.
6491  * <pre><code>
6492 var RecordDef = Roo.data.Record.create([
6493     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6494     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6495 ]);
6496 var myReader = new Roo.data.ArrayReader({
6497     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6498 }, RecordDef);
6499 </code></pre>
6500  * <p>
6501  * This would consume an Array like this:
6502  * <pre><code>
6503 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6504   </code></pre>
6505  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6506  * @constructor
6507  * Create a new JsonReader
6508  * @param {Object} meta Metadata configuration options.
6509  * @param {Object} recordType Either an Array of field definition objects
6510  * as specified to {@link Roo.data.Record#create},
6511  * or an {@link Roo.data.Record} object
6512  * created using {@link Roo.data.Record#create}.
6513  */
6514 Roo.data.ArrayReader = function(meta, recordType){
6515     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6516 };
6517
6518 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6519     /**
6520      * Create a data block containing Roo.data.Records from an XML document.
6521      * @param {Object} o An Array of row objects which represents the dataset.
6522      * @return {Object} data A data block which is used by an Roo.data.Store object as
6523      * a cache of Roo.data.Records.
6524      */
6525     readRecords : function(o){
6526         var sid = this.meta ? this.meta.id : null;
6527         var recordType = this.recordType, fields = recordType.prototype.fields;
6528         var records = [];
6529         var root = o;
6530             for(var i = 0; i < root.length; i++){
6531                     var n = root[i];
6532                 var values = {};
6533                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6534                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6535                 var f = fields.items[j];
6536                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6537                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6538                 v = f.convert(v);
6539                 values[f.name] = v;
6540             }
6541                 var record = new recordType(values, id);
6542                 record.json = n;
6543                 records[records.length] = record;
6544             }
6545             return {
6546                 records : records,
6547                 totalRecords : records.length
6548             };
6549     }
6550 });/*
6551  * Based on:
6552  * Ext JS Library 1.1.1
6553  * Copyright(c) 2006-2007, Ext JS, LLC.
6554  *
6555  * Originally Released Under LGPL - original licence link has changed is not relivant.
6556  *
6557  * Fork - LGPL
6558  * <script type="text/javascript">
6559  */
6560
6561
6562 /**
6563  * @class Roo.data.Tree
6564  * @extends Roo.util.Observable
6565  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6566  * in the tree have most standard DOM functionality.
6567  * @constructor
6568  * @param {Node} root (optional) The root node
6569  */
6570 Roo.data.Tree = function(root){
6571    this.nodeHash = {};
6572    /**
6573     * The root node for this tree
6574     * @type Node
6575     */
6576    this.root = null;
6577    if(root){
6578        this.setRootNode(root);
6579    }
6580    this.addEvents({
6581        /**
6582         * @event append
6583         * Fires when a new child node is appended to a node in this tree.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The newly appended node
6587         * @param {Number} index The index of the newly appended node
6588         */
6589        "append" : true,
6590        /**
6591         * @event remove
6592         * Fires when a child node is removed from a node in this tree.
6593         * @param {Tree} tree The owner tree
6594         * @param {Node} parent The parent node
6595         * @param {Node} node The child node removed
6596         */
6597        "remove" : true,
6598        /**
6599         * @event move
6600         * Fires when a node is moved to a new location in the tree
6601         * @param {Tree} tree The owner tree
6602         * @param {Node} node The node moved
6603         * @param {Node} oldParent The old parent of this node
6604         * @param {Node} newParent The new parent of this node
6605         * @param {Number} index The index it was moved to
6606         */
6607        "move" : true,
6608        /**
6609         * @event insert
6610         * Fires when a new child node is inserted in a node in this tree.
6611         * @param {Tree} tree The owner tree
6612         * @param {Node} parent The parent node
6613         * @param {Node} node The child node inserted
6614         * @param {Node} refNode The child node the node was inserted before
6615         */
6616        "insert" : true,
6617        /**
6618         * @event beforeappend
6619         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6620         * @param {Tree} tree The owner tree
6621         * @param {Node} parent The parent node
6622         * @param {Node} node The child node to be appended
6623         */
6624        "beforeappend" : true,
6625        /**
6626         * @event beforeremove
6627         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6628         * @param {Tree} tree The owner tree
6629         * @param {Node} parent The parent node
6630         * @param {Node} node The child node to be removed
6631         */
6632        "beforeremove" : true,
6633        /**
6634         * @event beforemove
6635         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6636         * @param {Tree} tree The owner tree
6637         * @param {Node} node The node being moved
6638         * @param {Node} oldParent The parent of the node
6639         * @param {Node} newParent The new parent the node is moving to
6640         * @param {Number} index The index it is being moved to
6641         */
6642        "beforemove" : true,
6643        /**
6644         * @event beforeinsert
6645         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6646         * @param {Tree} tree The owner tree
6647         * @param {Node} parent The parent node
6648         * @param {Node} node The child node to be inserted
6649         * @param {Node} refNode The child node the node is being inserted before
6650         */
6651        "beforeinsert" : true
6652    });
6653
6654     Roo.data.Tree.superclass.constructor.call(this);
6655 };
6656
6657 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6658     pathSeparator: "/",
6659
6660     proxyNodeEvent : function(){
6661         return this.fireEvent.apply(this, arguments);
6662     },
6663
6664     /**
6665      * Returns the root node for this tree.
6666      * @return {Node}
6667      */
6668     getRootNode : function(){
6669         return this.root;
6670     },
6671
6672     /**
6673      * Sets the root node for this tree.
6674      * @param {Node} node
6675      * @return {Node}
6676      */
6677     setRootNode : function(node){
6678         this.root = node;
6679         node.ownerTree = this;
6680         node.isRoot = true;
6681         this.registerNode(node);
6682         return node;
6683     },
6684
6685     /**
6686      * Gets a node in this tree by its id.
6687      * @param {String} id
6688      * @return {Node}
6689      */
6690     getNodeById : function(id){
6691         return this.nodeHash[id];
6692     },
6693
6694     registerNode : function(node){
6695         this.nodeHash[node.id] = node;
6696     },
6697
6698     unregisterNode : function(node){
6699         delete this.nodeHash[node.id];
6700     },
6701
6702     toString : function(){
6703         return "[Tree"+(this.id?" "+this.id:"")+"]";
6704     }
6705 });
6706
6707 /**
6708  * @class Roo.data.Node
6709  * @extends Roo.util.Observable
6710  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6711  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6712  * @constructor
6713  * @param {Object} attributes The attributes/config for the node
6714  */
6715 Roo.data.Node = function(attributes){
6716     /**
6717      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6718      * @type {Object}
6719      */
6720     this.attributes = attributes || {};
6721     this.leaf = this.attributes.leaf;
6722     /**
6723      * The node id. @type String
6724      */
6725     this.id = this.attributes.id;
6726     if(!this.id){
6727         this.id = Roo.id(null, "ynode-");
6728         this.attributes.id = this.id;
6729     }
6730      
6731     
6732     /**
6733      * All child nodes of this node. @type Array
6734      */
6735     this.childNodes = [];
6736     if(!this.childNodes.indexOf){ // indexOf is a must
6737         this.childNodes.indexOf = function(o){
6738             for(var i = 0, len = this.length; i < len; i++){
6739                 if(this[i] == o) {
6740                     return i;
6741                 }
6742             }
6743             return -1;
6744         };
6745     }
6746     /**
6747      * The parent node for this node. @type Node
6748      */
6749     this.parentNode = null;
6750     /**
6751      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6752      */
6753     this.firstChild = null;
6754     /**
6755      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6756      */
6757     this.lastChild = null;
6758     /**
6759      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6760      */
6761     this.previousSibling = null;
6762     /**
6763      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6764      */
6765     this.nextSibling = null;
6766
6767     this.addEvents({
6768        /**
6769         * @event append
6770         * Fires when a new child node is appended
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} node The newly appended node
6774         * @param {Number} index The index of the newly appended node
6775         */
6776        "append" : true,
6777        /**
6778         * @event remove
6779         * Fires when a child node is removed
6780         * @param {Tree} tree The owner tree
6781         * @param {Node} this This node
6782         * @param {Node} node The removed node
6783         */
6784        "remove" : true,
6785        /**
6786         * @event move
6787         * Fires when this node is moved to a new location in the tree
6788         * @param {Tree} tree The owner tree
6789         * @param {Node} this This node
6790         * @param {Node} oldParent The old parent of this node
6791         * @param {Node} newParent The new parent of this node
6792         * @param {Number} index The index it was moved to
6793         */
6794        "move" : true,
6795        /**
6796         * @event insert
6797         * Fires when a new child node is inserted.
6798         * @param {Tree} tree The owner tree
6799         * @param {Node} this This node
6800         * @param {Node} node The child node inserted
6801         * @param {Node} refNode The child node the node was inserted before
6802         */
6803        "insert" : true,
6804        /**
6805         * @event beforeappend
6806         * Fires before a new child is appended, return false to cancel the append.
6807         * @param {Tree} tree The owner tree
6808         * @param {Node} this This node
6809         * @param {Node} node The child node to be appended
6810         */
6811        "beforeappend" : true,
6812        /**
6813         * @event beforeremove
6814         * Fires before a child is removed, return false to cancel the remove.
6815         * @param {Tree} tree The owner tree
6816         * @param {Node} this This node
6817         * @param {Node} node The child node to be removed
6818         */
6819        "beforeremove" : true,
6820        /**
6821         * @event beforemove
6822         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6823         * @param {Tree} tree The owner tree
6824         * @param {Node} this This node
6825         * @param {Node} oldParent The parent of this node
6826         * @param {Node} newParent The new parent this node is moving to
6827         * @param {Number} index The index it is being moved to
6828         */
6829        "beforemove" : true,
6830        /**
6831         * @event beforeinsert
6832         * Fires before a new child is inserted, return false to cancel the insert.
6833         * @param {Tree} tree The owner tree
6834         * @param {Node} this This node
6835         * @param {Node} node The child node to be inserted
6836         * @param {Node} refNode The child node the node is being inserted before
6837         */
6838        "beforeinsert" : true
6839    });
6840     this.listeners = this.attributes.listeners;
6841     Roo.data.Node.superclass.constructor.call(this);
6842 };
6843
6844 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6845     fireEvent : function(evtName){
6846         // first do standard event for this node
6847         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6848             return false;
6849         }
6850         // then bubble it up to the tree if the event wasn't cancelled
6851         var ot = this.getOwnerTree();
6852         if(ot){
6853             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6854                 return false;
6855             }
6856         }
6857         return true;
6858     },
6859
6860     /**
6861      * Returns true if this node is a leaf
6862      * @return {Boolean}
6863      */
6864     isLeaf : function(){
6865         return this.leaf === true;
6866     },
6867
6868     // private
6869     setFirstChild : function(node){
6870         this.firstChild = node;
6871     },
6872
6873     //private
6874     setLastChild : function(node){
6875         this.lastChild = node;
6876     },
6877
6878
6879     /**
6880      * Returns true if this node is the last child of its parent
6881      * @return {Boolean}
6882      */
6883     isLast : function(){
6884        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6885     },
6886
6887     /**
6888      * Returns true if this node is the first child of its parent
6889      * @return {Boolean}
6890      */
6891     isFirst : function(){
6892        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6893     },
6894
6895     hasChildNodes : function(){
6896         return !this.isLeaf() && this.childNodes.length > 0;
6897     },
6898
6899     /**
6900      * Insert node(s) as the last child node of this node.
6901      * @param {Node/Array} node The node or Array of nodes to append
6902      * @return {Node} The appended node if single append, or null if an array was passed
6903      */
6904     appendChild : function(node){
6905         var multi = false;
6906         if(node instanceof Array){
6907             multi = node;
6908         }else if(arguments.length > 1){
6909             multi = arguments;
6910         }
6911         // if passed an array or multiple args do them one by one
6912         if(multi){
6913             for(var i = 0, len = multi.length; i < len; i++) {
6914                 this.appendChild(multi[i]);
6915             }
6916         }else{
6917             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6918                 return false;
6919             }
6920             var index = this.childNodes.length;
6921             var oldParent = node.parentNode;
6922             // it's a move, make sure we move it cleanly
6923             if(oldParent){
6924                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6925                     return false;
6926                 }
6927                 oldParent.removeChild(node);
6928             }
6929             index = this.childNodes.length;
6930             if(index == 0){
6931                 this.setFirstChild(node);
6932             }
6933             this.childNodes.push(node);
6934             node.parentNode = this;
6935             var ps = this.childNodes[index-1];
6936             if(ps){
6937                 node.previousSibling = ps;
6938                 ps.nextSibling = node;
6939             }else{
6940                 node.previousSibling = null;
6941             }
6942             node.nextSibling = null;
6943             this.setLastChild(node);
6944             node.setOwnerTree(this.getOwnerTree());
6945             this.fireEvent("append", this.ownerTree, this, node, index);
6946             if(oldParent){
6947                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6948             }
6949             return node;
6950         }
6951     },
6952
6953     /**
6954      * Removes a child node from this node.
6955      * @param {Node} node The node to remove
6956      * @return {Node} The removed node
6957      */
6958     removeChild : function(node){
6959         var index = this.childNodes.indexOf(node);
6960         if(index == -1){
6961             return false;
6962         }
6963         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6964             return false;
6965         }
6966
6967         // remove it from childNodes collection
6968         this.childNodes.splice(index, 1);
6969
6970         // update siblings
6971         if(node.previousSibling){
6972             node.previousSibling.nextSibling = node.nextSibling;
6973         }
6974         if(node.nextSibling){
6975             node.nextSibling.previousSibling = node.previousSibling;
6976         }
6977
6978         // update child refs
6979         if(this.firstChild == node){
6980             this.setFirstChild(node.nextSibling);
6981         }
6982         if(this.lastChild == node){
6983             this.setLastChild(node.previousSibling);
6984         }
6985
6986         node.setOwnerTree(null);
6987         // clear any references from the node
6988         node.parentNode = null;
6989         node.previousSibling = null;
6990         node.nextSibling = null;
6991         this.fireEvent("remove", this.ownerTree, this, node);
6992         return node;
6993     },
6994
6995     /**
6996      * Inserts the first node before the second node in this nodes childNodes collection.
6997      * @param {Node} node The node to insert
6998      * @param {Node} refNode The node to insert before (if null the node is appended)
6999      * @return {Node} The inserted node
7000      */
7001     insertBefore : function(node, refNode){
7002         if(!refNode){ // like standard Dom, refNode can be null for append
7003             return this.appendChild(node);
7004         }
7005         // nothing to do
7006         if(node == refNode){
7007             return false;
7008         }
7009
7010         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7011             return false;
7012         }
7013         var index = this.childNodes.indexOf(refNode);
7014         var oldParent = node.parentNode;
7015         var refIndex = index;
7016
7017         // when moving internally, indexes will change after remove
7018         if(oldParent == this && this.childNodes.indexOf(node) < index){
7019             refIndex--;
7020         }
7021
7022         // it's a move, make sure we move it cleanly
7023         if(oldParent){
7024             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7025                 return false;
7026             }
7027             oldParent.removeChild(node);
7028         }
7029         if(refIndex == 0){
7030             this.setFirstChild(node);
7031         }
7032         this.childNodes.splice(refIndex, 0, node);
7033         node.parentNode = this;
7034         var ps = this.childNodes[refIndex-1];
7035         if(ps){
7036             node.previousSibling = ps;
7037             ps.nextSibling = node;
7038         }else{
7039             node.previousSibling = null;
7040         }
7041         node.nextSibling = refNode;
7042         refNode.previousSibling = node;
7043         node.setOwnerTree(this.getOwnerTree());
7044         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7045         if(oldParent){
7046             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7047         }
7048         return node;
7049     },
7050
7051     /**
7052      * Returns the child node at the specified index.
7053      * @param {Number} index
7054      * @return {Node}
7055      */
7056     item : function(index){
7057         return this.childNodes[index];
7058     },
7059
7060     /**
7061      * Replaces one child node in this node with another.
7062      * @param {Node} newChild The replacement node
7063      * @param {Node} oldChild The node to replace
7064      * @return {Node} The replaced node
7065      */
7066     replaceChild : function(newChild, oldChild){
7067         this.insertBefore(newChild, oldChild);
7068         this.removeChild(oldChild);
7069         return oldChild;
7070     },
7071
7072     /**
7073      * Returns the index of a child node
7074      * @param {Node} node
7075      * @return {Number} The index of the node or -1 if it was not found
7076      */
7077     indexOf : function(child){
7078         return this.childNodes.indexOf(child);
7079     },
7080
7081     /**
7082      * Returns the tree this node is in.
7083      * @return {Tree}
7084      */
7085     getOwnerTree : function(){
7086         // if it doesn't have one, look for one
7087         if(!this.ownerTree){
7088             var p = this;
7089             while(p){
7090                 if(p.ownerTree){
7091                     this.ownerTree = p.ownerTree;
7092                     break;
7093                 }
7094                 p = p.parentNode;
7095             }
7096         }
7097         return this.ownerTree;
7098     },
7099
7100     /**
7101      * Returns depth of this node (the root node has a depth of 0)
7102      * @return {Number}
7103      */
7104     getDepth : function(){
7105         var depth = 0;
7106         var p = this;
7107         while(p.parentNode){
7108             ++depth;
7109             p = p.parentNode;
7110         }
7111         return depth;
7112     },
7113
7114     // private
7115     setOwnerTree : function(tree){
7116         // if it's move, we need to update everyone
7117         if(tree != this.ownerTree){
7118             if(this.ownerTree){
7119                 this.ownerTree.unregisterNode(this);
7120             }
7121             this.ownerTree = tree;
7122             var cs = this.childNodes;
7123             for(var i = 0, len = cs.length; i < len; i++) {
7124                 cs[i].setOwnerTree(tree);
7125             }
7126             if(tree){
7127                 tree.registerNode(this);
7128             }
7129         }
7130     },
7131
7132     /**
7133      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7134      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7135      * @return {String} The path
7136      */
7137     getPath : function(attr){
7138         attr = attr || "id";
7139         var p = this.parentNode;
7140         var b = [this.attributes[attr]];
7141         while(p){
7142             b.unshift(p.attributes[attr]);
7143             p = p.parentNode;
7144         }
7145         var sep = this.getOwnerTree().pathSeparator;
7146         return sep + b.join(sep);
7147     },
7148
7149     /**
7150      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7151      * function call will be the scope provided or the current node. The arguments to the function
7152      * will be the args provided or the current node. If the function returns false at any point,
7153      * the bubble is stopped.
7154      * @param {Function} fn The function to call
7155      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7156      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7157      */
7158     bubble : function(fn, scope, args){
7159         var p = this;
7160         while(p){
7161             if(fn.call(scope || p, args || p) === false){
7162                 break;
7163             }
7164             p = p.parentNode;
7165         }
7166     },
7167
7168     /**
7169      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7170      * function call will be the scope provided or the current node. The arguments to the function
7171      * will be the args provided or the current node. If the function returns false at any point,
7172      * the cascade is stopped on that branch.
7173      * @param {Function} fn The function to call
7174      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7175      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7176      */
7177     cascade : function(fn, scope, args){
7178         if(fn.call(scope || this, args || this) !== false){
7179             var cs = this.childNodes;
7180             for(var i = 0, len = cs.length; i < len; i++) {
7181                 cs[i].cascade(fn, scope, args);
7182             }
7183         }
7184     },
7185
7186     /**
7187      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7188      * function call will be the scope provided or the current node. The arguments to the function
7189      * will be the args provided or the current node. If the function returns false at any point,
7190      * the iteration stops.
7191      * @param {Function} fn The function to call
7192      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7193      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7194      */
7195     eachChild : function(fn, scope, args){
7196         var cs = this.childNodes;
7197         for(var i = 0, len = cs.length; i < len; i++) {
7198                 if(fn.call(scope || this, args || cs[i]) === false){
7199                     break;
7200                 }
7201         }
7202     },
7203
7204     /**
7205      * Finds the first child that has the attribute with the specified value.
7206      * @param {String} attribute The attribute name
7207      * @param {Mixed} value The value to search for
7208      * @return {Node} The found child or null if none was found
7209      */
7210     findChild : function(attribute, value){
7211         var cs = this.childNodes;
7212         for(var i = 0, len = cs.length; i < len; i++) {
7213                 if(cs[i].attributes[attribute] == value){
7214                     return cs[i];
7215                 }
7216         }
7217         return null;
7218     },
7219
7220     /**
7221      * Finds the first child by a custom function. The child matches if the function passed
7222      * returns true.
7223      * @param {Function} fn
7224      * @param {Object} scope (optional)
7225      * @return {Node} The found child or null if none was found
7226      */
7227     findChildBy : function(fn, scope){
7228         var cs = this.childNodes;
7229         for(var i = 0, len = cs.length; i < len; i++) {
7230                 if(fn.call(scope||cs[i], cs[i]) === true){
7231                     return cs[i];
7232                 }
7233         }
7234         return null;
7235     },
7236
7237     /**
7238      * Sorts this nodes children using the supplied sort function
7239      * @param {Function} fn
7240      * @param {Object} scope (optional)
7241      */
7242     sort : function(fn, scope){
7243         var cs = this.childNodes;
7244         var len = cs.length;
7245         if(len > 0){
7246             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7247             cs.sort(sortFn);
7248             for(var i = 0; i < len; i++){
7249                 var n = cs[i];
7250                 n.previousSibling = cs[i-1];
7251                 n.nextSibling = cs[i+1];
7252                 if(i == 0){
7253                     this.setFirstChild(n);
7254                 }
7255                 if(i == len-1){
7256                     this.setLastChild(n);
7257                 }
7258             }
7259         }
7260     },
7261
7262     /**
7263      * Returns true if this node is an ancestor (at any point) of the passed node.
7264      * @param {Node} node
7265      * @return {Boolean}
7266      */
7267     contains : function(node){
7268         return node.isAncestor(this);
7269     },
7270
7271     /**
7272      * Returns true if the passed node is an ancestor (at any point) of this node.
7273      * @param {Node} node
7274      * @return {Boolean}
7275      */
7276     isAncestor : function(node){
7277         var p = this.parentNode;
7278         while(p){
7279             if(p == node){
7280                 return true;
7281             }
7282             p = p.parentNode;
7283         }
7284         return false;
7285     },
7286
7287     toString : function(){
7288         return "[Node"+(this.id?" "+this.id:"")+"]";
7289     }
7290 });/*
7291  * Based on:
7292  * Ext JS Library 1.1.1
7293  * Copyright(c) 2006-2007, Ext JS, LLC.
7294  *
7295  * Originally Released Under LGPL - original licence link has changed is not relivant.
7296  *
7297  * Fork - LGPL
7298  * <script type="text/javascript">
7299  */
7300  (function(){ 
7301 /**
7302  * @class Roo.Layer
7303  * @extends Roo.Element
7304  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7305  * automatic maintaining of shadow/shim positions.
7306  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7307  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7308  * you can pass a string with a CSS class name. False turns off the shadow.
7309  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7310  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7311  * @cfg {String} cls CSS class to add to the element
7312  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7313  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7314  * @constructor
7315  * @param {Object} config An object with config options.
7316  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7317  */
7318
7319 Roo.Layer = function(config, existingEl){
7320     config = config || {};
7321     var dh = Roo.DomHelper;
7322     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7323     if(existingEl){
7324         this.dom = Roo.getDom(existingEl);
7325     }
7326     if(!this.dom){
7327         var o = config.dh || {tag: "div", cls: "x-layer"};
7328         this.dom = dh.append(pel, o);
7329     }
7330     if(config.cls){
7331         this.addClass(config.cls);
7332     }
7333     this.constrain = config.constrain !== false;
7334     this.visibilityMode = Roo.Element.VISIBILITY;
7335     if(config.id){
7336         this.id = this.dom.id = config.id;
7337     }else{
7338         this.id = Roo.id(this.dom);
7339     }
7340     this.zindex = config.zindex || this.getZIndex();
7341     this.position("absolute", this.zindex);
7342     if(config.shadow){
7343         this.shadowOffset = config.shadowOffset || 4;
7344         this.shadow = new Roo.Shadow({
7345             offset : this.shadowOffset,
7346             mode : config.shadow
7347         });
7348     }else{
7349         this.shadowOffset = 0;
7350     }
7351     this.useShim = config.shim !== false && Roo.useShims;
7352     this.useDisplay = config.useDisplay;
7353     this.hide();
7354 };
7355
7356 var supr = Roo.Element.prototype;
7357
7358 // shims are shared among layer to keep from having 100 iframes
7359 var shims = [];
7360
7361 Roo.extend(Roo.Layer, Roo.Element, {
7362
7363     getZIndex : function(){
7364         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7365     },
7366
7367     getShim : function(){
7368         if(!this.useShim){
7369             return null;
7370         }
7371         if(this.shim){
7372             return this.shim;
7373         }
7374         var shim = shims.shift();
7375         if(!shim){
7376             shim = this.createShim();
7377             shim.enableDisplayMode('block');
7378             shim.dom.style.display = 'none';
7379             shim.dom.style.visibility = 'visible';
7380         }
7381         var pn = this.dom.parentNode;
7382         if(shim.dom.parentNode != pn){
7383             pn.insertBefore(shim.dom, this.dom);
7384         }
7385         shim.setStyle('z-index', this.getZIndex()-2);
7386         this.shim = shim;
7387         return shim;
7388     },
7389
7390     hideShim : function(){
7391         if(this.shim){
7392             this.shim.setDisplayed(false);
7393             shims.push(this.shim);
7394             delete this.shim;
7395         }
7396     },
7397
7398     disableShadow : function(){
7399         if(this.shadow){
7400             this.shadowDisabled = true;
7401             this.shadow.hide();
7402             this.lastShadowOffset = this.shadowOffset;
7403             this.shadowOffset = 0;
7404         }
7405     },
7406
7407     enableShadow : function(show){
7408         if(this.shadow){
7409             this.shadowDisabled = false;
7410             this.shadowOffset = this.lastShadowOffset;
7411             delete this.lastShadowOffset;
7412             if(show){
7413                 this.sync(true);
7414             }
7415         }
7416     },
7417
7418     // private
7419     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7420     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7421     sync : function(doShow){
7422         var sw = this.shadow;
7423         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7424             var sh = this.getShim();
7425
7426             var w = this.getWidth(),
7427                 h = this.getHeight();
7428
7429             var l = this.getLeft(true),
7430                 t = this.getTop(true);
7431
7432             if(sw && !this.shadowDisabled){
7433                 if(doShow && !sw.isVisible()){
7434                     sw.show(this);
7435                 }else{
7436                     sw.realign(l, t, w, h);
7437                 }
7438                 if(sh){
7439                     if(doShow){
7440                        sh.show();
7441                     }
7442                     // fit the shim behind the shadow, so it is shimmed too
7443                     var a = sw.adjusts, s = sh.dom.style;
7444                     s.left = (Math.min(l, l+a.l))+"px";
7445                     s.top = (Math.min(t, t+a.t))+"px";
7446                     s.width = (w+a.w)+"px";
7447                     s.height = (h+a.h)+"px";
7448                 }
7449             }else if(sh){
7450                 if(doShow){
7451                    sh.show();
7452                 }
7453                 sh.setSize(w, h);
7454                 sh.setLeftTop(l, t);
7455             }
7456             
7457         }
7458     },
7459
7460     // private
7461     destroy : function(){
7462         this.hideShim();
7463         if(this.shadow){
7464             this.shadow.hide();
7465         }
7466         this.removeAllListeners();
7467         var pn = this.dom.parentNode;
7468         if(pn){
7469             pn.removeChild(this.dom);
7470         }
7471         Roo.Element.uncache(this.id);
7472     },
7473
7474     remove : function(){
7475         this.destroy();
7476     },
7477
7478     // private
7479     beginUpdate : function(){
7480         this.updating = true;
7481     },
7482
7483     // private
7484     endUpdate : function(){
7485         this.updating = false;
7486         this.sync(true);
7487     },
7488
7489     // private
7490     hideUnders : function(negOffset){
7491         if(this.shadow){
7492             this.shadow.hide();
7493         }
7494         this.hideShim();
7495     },
7496
7497     // private
7498     constrainXY : function(){
7499         if(this.constrain){
7500             var vw = Roo.lib.Dom.getViewWidth(),
7501                 vh = Roo.lib.Dom.getViewHeight();
7502             var s = Roo.get(document).getScroll();
7503
7504             var xy = this.getXY();
7505             var x = xy[0], y = xy[1];   
7506             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7507             // only move it if it needs it
7508             var moved = false;
7509             // first validate right/bottom
7510             if((x + w) > vw+s.left){
7511                 x = vw - w - this.shadowOffset;
7512                 moved = true;
7513             }
7514             if((y + h) > vh+s.top){
7515                 y = vh - h - this.shadowOffset;
7516                 moved = true;
7517             }
7518             // then make sure top/left isn't negative
7519             if(x < s.left){
7520                 x = s.left;
7521                 moved = true;
7522             }
7523             if(y < s.top){
7524                 y = s.top;
7525                 moved = true;
7526             }
7527             if(moved){
7528                 if(this.avoidY){
7529                     var ay = this.avoidY;
7530                     if(y <= ay && (y+h) >= ay){
7531                         y = ay-h-5;   
7532                     }
7533                 }
7534                 xy = [x, y];
7535                 this.storeXY(xy);
7536                 supr.setXY.call(this, xy);
7537                 this.sync();
7538             }
7539         }
7540     },
7541
7542     isVisible : function(){
7543         return this.visible;    
7544     },
7545
7546     // private
7547     showAction : function(){
7548         this.visible = true; // track visibility to prevent getStyle calls
7549         if(this.useDisplay === true){
7550             this.setDisplayed("");
7551         }else if(this.lastXY){
7552             supr.setXY.call(this, this.lastXY);
7553         }else if(this.lastLT){
7554             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7555         }
7556     },
7557
7558     // private
7559     hideAction : function(){
7560         this.visible = false;
7561         if(this.useDisplay === true){
7562             this.setDisplayed(false);
7563         }else{
7564             this.setLeftTop(-10000,-10000);
7565         }
7566     },
7567
7568     // overridden Element method
7569     setVisible : function(v, a, d, c, e){
7570         if(v){
7571             this.showAction();
7572         }
7573         if(a && v){
7574             var cb = function(){
7575                 this.sync(true);
7576                 if(c){
7577                     c();
7578                 }
7579             }.createDelegate(this);
7580             supr.setVisible.call(this, true, true, d, cb, e);
7581         }else{
7582             if(!v){
7583                 this.hideUnders(true);
7584             }
7585             var cb = c;
7586             if(a){
7587                 cb = function(){
7588                     this.hideAction();
7589                     if(c){
7590                         c();
7591                     }
7592                 }.createDelegate(this);
7593             }
7594             supr.setVisible.call(this, v, a, d, cb, e);
7595             if(v){
7596                 this.sync(true);
7597             }else if(!a){
7598                 this.hideAction();
7599             }
7600         }
7601     },
7602
7603     storeXY : function(xy){
7604         delete this.lastLT;
7605         this.lastXY = xy;
7606     },
7607
7608     storeLeftTop : function(left, top){
7609         delete this.lastXY;
7610         this.lastLT = [left, top];
7611     },
7612
7613     // private
7614     beforeFx : function(){
7615         this.beforeAction();
7616         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7617     },
7618
7619     // private
7620     afterFx : function(){
7621         Roo.Layer.superclass.afterFx.apply(this, arguments);
7622         this.sync(this.isVisible());
7623     },
7624
7625     // private
7626     beforeAction : function(){
7627         if(!this.updating && this.shadow){
7628             this.shadow.hide();
7629         }
7630     },
7631
7632     // overridden Element method
7633     setLeft : function(left){
7634         this.storeLeftTop(left, this.getTop(true));
7635         supr.setLeft.apply(this, arguments);
7636         this.sync();
7637     },
7638
7639     setTop : function(top){
7640         this.storeLeftTop(this.getLeft(true), top);
7641         supr.setTop.apply(this, arguments);
7642         this.sync();
7643     },
7644
7645     setLeftTop : function(left, top){
7646         this.storeLeftTop(left, top);
7647         supr.setLeftTop.apply(this, arguments);
7648         this.sync();
7649     },
7650
7651     setXY : function(xy, a, d, c, e){
7652         this.fixDisplay();
7653         this.beforeAction();
7654         this.storeXY(xy);
7655         var cb = this.createCB(c);
7656         supr.setXY.call(this, xy, a, d, cb, e);
7657         if(!a){
7658             cb();
7659         }
7660     },
7661
7662     // private
7663     createCB : function(c){
7664         var el = this;
7665         return function(){
7666             el.constrainXY();
7667             el.sync(true);
7668             if(c){
7669                 c();
7670             }
7671         };
7672     },
7673
7674     // overridden Element method
7675     setX : function(x, a, d, c, e){
7676         this.setXY([x, this.getY()], a, d, c, e);
7677     },
7678
7679     // overridden Element method
7680     setY : function(y, a, d, c, e){
7681         this.setXY([this.getX(), y], a, d, c, e);
7682     },
7683
7684     // overridden Element method
7685     setSize : function(w, h, a, d, c, e){
7686         this.beforeAction();
7687         var cb = this.createCB(c);
7688         supr.setSize.call(this, w, h, a, d, cb, e);
7689         if(!a){
7690             cb();
7691         }
7692     },
7693
7694     // overridden Element method
7695     setWidth : function(w, a, d, c, e){
7696         this.beforeAction();
7697         var cb = this.createCB(c);
7698         supr.setWidth.call(this, w, a, d, cb, e);
7699         if(!a){
7700             cb();
7701         }
7702     },
7703
7704     // overridden Element method
7705     setHeight : function(h, a, d, c, e){
7706         this.beforeAction();
7707         var cb = this.createCB(c);
7708         supr.setHeight.call(this, h, a, d, cb, e);
7709         if(!a){
7710             cb();
7711         }
7712     },
7713
7714     // overridden Element method
7715     setBounds : function(x, y, w, h, a, d, c, e){
7716         this.beforeAction();
7717         var cb = this.createCB(c);
7718         if(!a){
7719             this.storeXY([x, y]);
7720             supr.setXY.call(this, [x, y]);
7721             supr.setSize.call(this, w, h, a, d, cb, e);
7722             cb();
7723         }else{
7724             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7725         }
7726         return this;
7727     },
7728     
7729     /**
7730      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7731      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7732      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7733      * @param {Number} zindex The new z-index to set
7734      * @return {this} The Layer
7735      */
7736     setZIndex : function(zindex){
7737         this.zindex = zindex;
7738         this.setStyle("z-index", zindex + 2);
7739         if(this.shadow){
7740             this.shadow.setZIndex(zindex + 1);
7741         }
7742         if(this.shim){
7743             this.shim.setStyle("z-index", zindex);
7744         }
7745     }
7746 });
7747 })();/*
7748  * Based on:
7749  * Ext JS Library 1.1.1
7750  * Copyright(c) 2006-2007, Ext JS, LLC.
7751  *
7752  * Originally Released Under LGPL - original licence link has changed is not relivant.
7753  *
7754  * Fork - LGPL
7755  * <script type="text/javascript">
7756  */
7757
7758
7759 /**
7760  * @class Roo.Shadow
7761  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7762  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7763  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7764  * @constructor
7765  * Create a new Shadow
7766  * @param {Object} config The config object
7767  */
7768 Roo.Shadow = function(config){
7769     Roo.apply(this, config);
7770     if(typeof this.mode != "string"){
7771         this.mode = this.defaultMode;
7772     }
7773     var o = this.offset, a = {h: 0};
7774     var rad = Math.floor(this.offset/2);
7775     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7776         case "drop":
7777             a.w = 0;
7778             a.l = a.t = o;
7779             a.t -= 1;
7780             if(Roo.isIE){
7781                 a.l -= this.offset + rad;
7782                 a.t -= this.offset + rad;
7783                 a.w -= rad;
7784                 a.h -= rad;
7785                 a.t += 1;
7786             }
7787         break;
7788         case "sides":
7789             a.w = (o*2);
7790             a.l = -o;
7791             a.t = o-1;
7792             if(Roo.isIE){
7793                 a.l -= (this.offset - rad);
7794                 a.t -= this.offset + rad;
7795                 a.l += 1;
7796                 a.w -= (this.offset - rad)*2;
7797                 a.w -= rad + 1;
7798                 a.h -= 1;
7799             }
7800         break;
7801         case "frame":
7802             a.w = a.h = (o*2);
7803             a.l = a.t = -o;
7804             a.t += 1;
7805             a.h -= 2;
7806             if(Roo.isIE){
7807                 a.l -= (this.offset - rad);
7808                 a.t -= (this.offset - rad);
7809                 a.l += 1;
7810                 a.w -= (this.offset + rad + 1);
7811                 a.h -= (this.offset + rad);
7812                 a.h += 1;
7813             }
7814         break;
7815     };
7816
7817     this.adjusts = a;
7818 };
7819
7820 Roo.Shadow.prototype = {
7821     /**
7822      * @cfg {String} mode
7823      * The shadow display mode.  Supports the following options:<br />
7824      * sides: Shadow displays on both sides and bottom only<br />
7825      * frame: Shadow displays equally on all four sides<br />
7826      * drop: Traditional bottom-right drop shadow (default)
7827      */
7828     /**
7829      * @cfg {String} offset
7830      * The number of pixels to offset the shadow from the element (defaults to 4)
7831      */
7832     offset: 4,
7833
7834     // private
7835     defaultMode: "drop",
7836
7837     /**
7838      * Displays the shadow under the target element
7839      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7840      */
7841     show : function(target){
7842         target = Roo.get(target);
7843         if(!this.el){
7844             this.el = Roo.Shadow.Pool.pull();
7845             if(this.el.dom.nextSibling != target.dom){
7846                 this.el.insertBefore(target);
7847             }
7848         }
7849         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7850         if(Roo.isIE){
7851             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7852         }
7853         this.realign(
7854             target.getLeft(true),
7855             target.getTop(true),
7856             target.getWidth(),
7857             target.getHeight()
7858         );
7859         this.el.dom.style.display = "block";
7860     },
7861
7862     /**
7863      * Returns true if the shadow is visible, else false
7864      */
7865     isVisible : function(){
7866         return this.el ? true : false;  
7867     },
7868
7869     /**
7870      * Direct alignment when values are already available. Show must be called at least once before
7871      * calling this method to ensure it is initialized.
7872      * @param {Number} left The target element left position
7873      * @param {Number} top The target element top position
7874      * @param {Number} width The target element width
7875      * @param {Number} height The target element height
7876      */
7877     realign : function(l, t, w, h){
7878         if(!this.el){
7879             return;
7880         }
7881         var a = this.adjusts, d = this.el.dom, s = d.style;
7882         var iea = 0;
7883         s.left = (l+a.l)+"px";
7884         s.top = (t+a.t)+"px";
7885         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7886  
7887         if(s.width != sws || s.height != shs){
7888             s.width = sws;
7889             s.height = shs;
7890             if(!Roo.isIE){
7891                 var cn = d.childNodes;
7892                 var sww = Math.max(0, (sw-12))+"px";
7893                 cn[0].childNodes[1].style.width = sww;
7894                 cn[1].childNodes[1].style.width = sww;
7895                 cn[2].childNodes[1].style.width = sww;
7896                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7897             }
7898         }
7899     },
7900
7901     /**
7902      * Hides this shadow
7903      */
7904     hide : function(){
7905         if(this.el){
7906             this.el.dom.style.display = "none";
7907             Roo.Shadow.Pool.push(this.el);
7908             delete this.el;
7909         }
7910     },
7911
7912     /**
7913      * Adjust the z-index of this shadow
7914      * @param {Number} zindex The new z-index
7915      */
7916     setZIndex : function(z){
7917         this.zIndex = z;
7918         if(this.el){
7919             this.el.setStyle("z-index", z);
7920         }
7921     }
7922 };
7923
7924 // Private utility class that manages the internal Shadow cache
7925 Roo.Shadow.Pool = function(){
7926     var p = [];
7927     var markup = Roo.isIE ?
7928                  '<div class="x-ie-shadow"></div>' :
7929                  '<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>';
7930     return {
7931         pull : function(){
7932             var sh = p.shift();
7933             if(!sh){
7934                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7935                 sh.autoBoxAdjust = false;
7936             }
7937             return sh;
7938         },
7939
7940         push : function(sh){
7941             p.push(sh);
7942         }
7943     };
7944 }();/*
7945  * Based on:
7946  * Ext JS Library 1.1.1
7947  * Copyright(c) 2006-2007, Ext JS, LLC.
7948  *
7949  * Originally Released Under LGPL - original licence link has changed is not relivant.
7950  *
7951  * Fork - LGPL
7952  * <script type="text/javascript">
7953  */
7954
7955
7956 /**
7957  * @class Roo.SplitBar
7958  * @extends Roo.util.Observable
7959  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7960  * <br><br>
7961  * Usage:
7962  * <pre><code>
7963 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7964                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7965 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7966 split.minSize = 100;
7967 split.maxSize = 600;
7968 split.animate = true;
7969 split.on('moved', splitterMoved);
7970 </code></pre>
7971  * @constructor
7972  * Create a new SplitBar
7973  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7974  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7975  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7976  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7977                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7978                         position of the SplitBar).
7979  */
7980 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7981     
7982     /** @private */
7983     this.el = Roo.get(dragElement, true);
7984     this.el.dom.unselectable = "on";
7985     /** @private */
7986     this.resizingEl = Roo.get(resizingElement, true);
7987
7988     /**
7989      * @private
7990      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7991      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7992      * @type Number
7993      */
7994     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7995     
7996     /**
7997      * The minimum size of the resizing element. (Defaults to 0)
7998      * @type Number
7999      */
8000     this.minSize = 0;
8001     
8002     /**
8003      * The maximum size of the resizing element. (Defaults to 2000)
8004      * @type Number
8005      */
8006     this.maxSize = 2000;
8007     
8008     /**
8009      * Whether to animate the transition to the new size
8010      * @type Boolean
8011      */
8012     this.animate = false;
8013     
8014     /**
8015      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8016      * @type Boolean
8017      */
8018     this.useShim = false;
8019     
8020     /** @private */
8021     this.shim = null;
8022     
8023     if(!existingProxy){
8024         /** @private */
8025         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8026     }else{
8027         this.proxy = Roo.get(existingProxy).dom;
8028     }
8029     /** @private */
8030     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8031     
8032     /** @private */
8033     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8034     
8035     /** @private */
8036     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8037     
8038     /** @private */
8039     this.dragSpecs = {};
8040     
8041     /**
8042      * @private The adapter to use to positon and resize elements
8043      */
8044     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8045     this.adapter.init(this);
8046     
8047     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8048         /** @private */
8049         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8050         this.el.addClass("x-splitbar-h");
8051     }else{
8052         /** @private */
8053         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8054         this.el.addClass("x-splitbar-v");
8055     }
8056     
8057     this.addEvents({
8058         /**
8059          * @event resize
8060          * Fires when the splitter is moved (alias for {@link #event-moved})
8061          * @param {Roo.SplitBar} this
8062          * @param {Number} newSize the new width or height
8063          */
8064         "resize" : true,
8065         /**
8066          * @event moved
8067          * Fires when the splitter is moved
8068          * @param {Roo.SplitBar} this
8069          * @param {Number} newSize the new width or height
8070          */
8071         "moved" : true,
8072         /**
8073          * @event beforeresize
8074          * Fires before the splitter is dragged
8075          * @param {Roo.SplitBar} this
8076          */
8077         "beforeresize" : true,
8078
8079         "beforeapply" : true
8080     });
8081
8082     Roo.util.Observable.call(this);
8083 };
8084
8085 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8086     onStartProxyDrag : function(x, y){
8087         this.fireEvent("beforeresize", this);
8088         if(!this.overlay){
8089             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8090             o.unselectable();
8091             o.enableDisplayMode("block");
8092             // all splitbars share the same overlay
8093             Roo.SplitBar.prototype.overlay = o;
8094         }
8095         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8096         this.overlay.show();
8097         Roo.get(this.proxy).setDisplayed("block");
8098         var size = this.adapter.getElementSize(this);
8099         this.activeMinSize = this.getMinimumSize();;
8100         this.activeMaxSize = this.getMaximumSize();;
8101         var c1 = size - this.activeMinSize;
8102         var c2 = Math.max(this.activeMaxSize - size, 0);
8103         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8104             this.dd.resetConstraints();
8105             this.dd.setXConstraint(
8106                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8107                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8108             );
8109             this.dd.setYConstraint(0, 0);
8110         }else{
8111             this.dd.resetConstraints();
8112             this.dd.setXConstraint(0, 0);
8113             this.dd.setYConstraint(
8114                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8115                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8116             );
8117          }
8118         this.dragSpecs.startSize = size;
8119         this.dragSpecs.startPoint = [x, y];
8120         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8121     },
8122     
8123     /** 
8124      * @private Called after the drag operation by the DDProxy
8125      */
8126     onEndProxyDrag : function(e){
8127         Roo.get(this.proxy).setDisplayed(false);
8128         var endPoint = Roo.lib.Event.getXY(e);
8129         if(this.overlay){
8130             this.overlay.hide();
8131         }
8132         var newSize;
8133         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8134             newSize = this.dragSpecs.startSize + 
8135                 (this.placement == Roo.SplitBar.LEFT ?
8136                     endPoint[0] - this.dragSpecs.startPoint[0] :
8137                     this.dragSpecs.startPoint[0] - endPoint[0]
8138                 );
8139         }else{
8140             newSize = this.dragSpecs.startSize + 
8141                 (this.placement == Roo.SplitBar.TOP ?
8142                     endPoint[1] - this.dragSpecs.startPoint[1] :
8143                     this.dragSpecs.startPoint[1] - endPoint[1]
8144                 );
8145         }
8146         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8147         if(newSize != this.dragSpecs.startSize){
8148             if(this.fireEvent('beforeapply', this, newSize) !== false){
8149                 this.adapter.setElementSize(this, newSize);
8150                 this.fireEvent("moved", this, newSize);
8151                 this.fireEvent("resize", this, newSize);
8152             }
8153         }
8154     },
8155     
8156     /**
8157      * Get the adapter this SplitBar uses
8158      * @return The adapter object
8159      */
8160     getAdapter : function(){
8161         return this.adapter;
8162     },
8163     
8164     /**
8165      * Set the adapter this SplitBar uses
8166      * @param {Object} adapter A SplitBar adapter object
8167      */
8168     setAdapter : function(adapter){
8169         this.adapter = adapter;
8170         this.adapter.init(this);
8171     },
8172     
8173     /**
8174      * Gets the minimum size for the resizing element
8175      * @return {Number} The minimum size
8176      */
8177     getMinimumSize : function(){
8178         return this.minSize;
8179     },
8180     
8181     /**
8182      * Sets the minimum size for the resizing element
8183      * @param {Number} minSize The minimum size
8184      */
8185     setMinimumSize : function(minSize){
8186         this.minSize = minSize;
8187     },
8188     
8189     /**
8190      * Gets the maximum size for the resizing element
8191      * @return {Number} The maximum size
8192      */
8193     getMaximumSize : function(){
8194         return this.maxSize;
8195     },
8196     
8197     /**
8198      * Sets the maximum size for the resizing element
8199      * @param {Number} maxSize The maximum size
8200      */
8201     setMaximumSize : function(maxSize){
8202         this.maxSize = maxSize;
8203     },
8204     
8205     /**
8206      * Sets the initialize size for the resizing element
8207      * @param {Number} size The initial size
8208      */
8209     setCurrentSize : function(size){
8210         var oldAnimate = this.animate;
8211         this.animate = false;
8212         this.adapter.setElementSize(this, size);
8213         this.animate = oldAnimate;
8214     },
8215     
8216     /**
8217      * Destroy this splitbar. 
8218      * @param {Boolean} removeEl True to remove the element
8219      */
8220     destroy : function(removeEl){
8221         if(this.shim){
8222             this.shim.remove();
8223         }
8224         this.dd.unreg();
8225         this.proxy.parentNode.removeChild(this.proxy);
8226         if(removeEl){
8227             this.el.remove();
8228         }
8229     }
8230 });
8231
8232 /**
8233  * @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.
8234  */
8235 Roo.SplitBar.createProxy = function(dir){
8236     var proxy = new Roo.Element(document.createElement("div"));
8237     proxy.unselectable();
8238     var cls = 'x-splitbar-proxy';
8239     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8240     document.body.appendChild(proxy.dom);
8241     return proxy.dom;
8242 };
8243
8244 /** 
8245  * @class Roo.SplitBar.BasicLayoutAdapter
8246  * Default Adapter. It assumes the splitter and resizing element are not positioned
8247  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8248  */
8249 Roo.SplitBar.BasicLayoutAdapter = function(){
8250 };
8251
8252 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8253     // do nothing for now
8254     init : function(s){
8255     
8256     },
8257     /**
8258      * Called before drag operations to get the current size of the resizing element. 
8259      * @param {Roo.SplitBar} s The SplitBar using this adapter
8260      */
8261      getElementSize : function(s){
8262         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8263             return s.resizingEl.getWidth();
8264         }else{
8265             return s.resizingEl.getHeight();
8266         }
8267     },
8268     
8269     /**
8270      * Called after drag operations to set the size of the resizing element.
8271      * @param {Roo.SplitBar} s The SplitBar using this adapter
8272      * @param {Number} newSize The new size to set
8273      * @param {Function} onComplete A function to be invoked when resizing is complete
8274      */
8275     setElementSize : function(s, newSize, onComplete){
8276         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8277             if(!s.animate){
8278                 s.resizingEl.setWidth(newSize);
8279                 if(onComplete){
8280                     onComplete(s, newSize);
8281                 }
8282             }else{
8283                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8284             }
8285         }else{
8286             
8287             if(!s.animate){
8288                 s.resizingEl.setHeight(newSize);
8289                 if(onComplete){
8290                     onComplete(s, newSize);
8291                 }
8292             }else{
8293                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8294             }
8295         }
8296     }
8297 };
8298
8299 /** 
8300  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8301  * @extends Roo.SplitBar.BasicLayoutAdapter
8302  * Adapter that  moves the splitter element to align with the resized sizing element. 
8303  * Used with an absolute positioned SplitBar.
8304  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8305  * document.body, make sure you assign an id to the body element.
8306  */
8307 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8308     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8309     this.container = Roo.get(container);
8310 };
8311
8312 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8313     init : function(s){
8314         this.basic.init(s);
8315     },
8316     
8317     getElementSize : function(s){
8318         return this.basic.getElementSize(s);
8319     },
8320     
8321     setElementSize : function(s, newSize, onComplete){
8322         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8323     },
8324     
8325     moveSplitter : function(s){
8326         var yes = Roo.SplitBar;
8327         switch(s.placement){
8328             case yes.LEFT:
8329                 s.el.setX(s.resizingEl.getRight());
8330                 break;
8331             case yes.RIGHT:
8332                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8333                 break;
8334             case yes.TOP:
8335                 s.el.setY(s.resizingEl.getBottom());
8336                 break;
8337             case yes.BOTTOM:
8338                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8339                 break;
8340         }
8341     }
8342 };
8343
8344 /**
8345  * Orientation constant - Create a vertical SplitBar
8346  * @static
8347  * @type Number
8348  */
8349 Roo.SplitBar.VERTICAL = 1;
8350
8351 /**
8352  * Orientation constant - Create a horizontal SplitBar
8353  * @static
8354  * @type Number
8355  */
8356 Roo.SplitBar.HORIZONTAL = 2;
8357
8358 /**
8359  * Placement constant - The resizing element is to the left of the splitter element
8360  * @static
8361  * @type Number
8362  */
8363 Roo.SplitBar.LEFT = 1;
8364
8365 /**
8366  * Placement constant - The resizing element is to the right of the splitter element
8367  * @static
8368  * @type Number
8369  */
8370 Roo.SplitBar.RIGHT = 2;
8371
8372 /**
8373  * Placement constant - The resizing element is positioned above the splitter element
8374  * @static
8375  * @type Number
8376  */
8377 Roo.SplitBar.TOP = 3;
8378
8379 /**
8380  * Placement constant - The resizing element is positioned under splitter element
8381  * @static
8382  * @type Number
8383  */
8384 Roo.SplitBar.BOTTOM = 4;
8385 /*
8386  * Based on:
8387  * Ext JS Library 1.1.1
8388  * Copyright(c) 2006-2007, Ext JS, LLC.
8389  *
8390  * Originally Released Under LGPL - original licence link has changed is not relivant.
8391  *
8392  * Fork - LGPL
8393  * <script type="text/javascript">
8394  */
8395
8396 /**
8397  * @class Roo.View
8398  * @extends Roo.util.Observable
8399  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8400  * This class also supports single and multi selection modes. <br>
8401  * Create a data model bound view:
8402  <pre><code>
8403  var store = new Roo.data.Store(...);
8404
8405  var view = new Roo.View({
8406     el : "my-element",
8407     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8408  
8409     singleSelect: true,
8410     selectedClass: "ydataview-selected",
8411     store: store
8412  });
8413
8414  // listen for node click?
8415  view.on("click", function(vw, index, node, e){
8416  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8417  });
8418
8419  // load XML data
8420  dataModel.load("foobar.xml");
8421  </code></pre>
8422  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8423  * <br><br>
8424  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8425  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8426  * 
8427  * Note: old style constructor is still suported (container, template, config)
8428  * 
8429  * @constructor
8430  * Create a new View
8431  * @param {Object} config The config object
8432  * 
8433  */
8434 Roo.View = function(config, depreciated_tpl, depreciated_config){
8435     
8436     this.parent = false;
8437     
8438     if (typeof(depreciated_tpl) == 'undefined') {
8439         // new way.. - universal constructor.
8440         Roo.apply(this, config);
8441         this.el  = Roo.get(this.el);
8442     } else {
8443         // old format..
8444         this.el  = Roo.get(config);
8445         this.tpl = depreciated_tpl;
8446         Roo.apply(this, depreciated_config);
8447     }
8448     this.wrapEl  = this.el.wrap().wrap();
8449     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8450     
8451     
8452     if(typeof(this.tpl) == "string"){
8453         this.tpl = new Roo.Template(this.tpl);
8454     } else {
8455         // support xtype ctors..
8456         this.tpl = new Roo.factory(this.tpl, Roo);
8457     }
8458     
8459     
8460     this.tpl.compile();
8461     
8462     /** @private */
8463     this.addEvents({
8464         /**
8465          * @event beforeclick
8466          * Fires before a click is processed. Returns false to cancel the default action.
8467          * @param {Roo.View} this
8468          * @param {Number} index The index of the target node
8469          * @param {HTMLElement} node The target node
8470          * @param {Roo.EventObject} e The raw event object
8471          */
8472             "beforeclick" : true,
8473         /**
8474          * @event click
8475          * Fires when a template node is clicked.
8476          * @param {Roo.View} this
8477          * @param {Number} index The index of the target node
8478          * @param {HTMLElement} node The target node
8479          * @param {Roo.EventObject} e The raw event object
8480          */
8481             "click" : true,
8482         /**
8483          * @event dblclick
8484          * Fires when a template node is double clicked.
8485          * @param {Roo.View} this
8486          * @param {Number} index The index of the target node
8487          * @param {HTMLElement} node The target node
8488          * @param {Roo.EventObject} e The raw event object
8489          */
8490             "dblclick" : true,
8491         /**
8492          * @event contextmenu
8493          * Fires when a template node is right clicked.
8494          * @param {Roo.View} this
8495          * @param {Number} index The index of the target node
8496          * @param {HTMLElement} node The target node
8497          * @param {Roo.EventObject} e The raw event object
8498          */
8499             "contextmenu" : true,
8500         /**
8501          * @event selectionchange
8502          * Fires when the selected nodes change.
8503          * @param {Roo.View} this
8504          * @param {Array} selections Array of the selected nodes
8505          */
8506             "selectionchange" : true,
8507     
8508         /**
8509          * @event beforeselect
8510          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8511          * @param {Roo.View} this
8512          * @param {HTMLElement} node The node to be selected
8513          * @param {Array} selections Array of currently selected nodes
8514          */
8515             "beforeselect" : true,
8516         /**
8517          * @event preparedata
8518          * Fires on every row to render, to allow you to change the data.
8519          * @param {Roo.View} this
8520          * @param {Object} data to be rendered (change this)
8521          */
8522           "preparedata" : true
8523           
8524           
8525         });
8526
8527
8528
8529     this.el.on({
8530         "click": this.onClick,
8531         "dblclick": this.onDblClick,
8532         "contextmenu": this.onContextMenu,
8533         scope:this
8534     });
8535
8536     this.selections = [];
8537     this.nodes = [];
8538     this.cmp = new Roo.CompositeElementLite([]);
8539     if(this.store){
8540         this.store = Roo.factory(this.store, Roo.data);
8541         this.setStore(this.store, true);
8542     }
8543     
8544     if ( this.footer && this.footer.xtype) {
8545            
8546          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8547         
8548         this.footer.dataSource = this.store;
8549         this.footer.container = fctr;
8550         this.footer = Roo.factory(this.footer, Roo);
8551         fctr.insertFirst(this.el);
8552         
8553         // this is a bit insane - as the paging toolbar seems to detach the el..
8554 //        dom.parentNode.parentNode.parentNode
8555          // they get detached?
8556     }
8557     
8558     
8559     Roo.View.superclass.constructor.call(this);
8560     
8561     
8562 };
8563
8564 Roo.extend(Roo.View, Roo.util.Observable, {
8565     
8566      /**
8567      * @cfg {Roo.data.Store} store Data store to load data from.
8568      */
8569     store : false,
8570     
8571     /**
8572      * @cfg {String|Roo.Element} el The container element.
8573      */
8574     el : '',
8575     
8576     /**
8577      * @cfg {String|Roo.Template} tpl The template used by this View 
8578      */
8579     tpl : false,
8580     /**
8581      * @cfg {String} dataName the named area of the template to use as the data area
8582      *                          Works with domtemplates roo-name="name"
8583      */
8584     dataName: false,
8585     /**
8586      * @cfg {String} selectedClass The css class to add to selected nodes
8587      */
8588     selectedClass : "x-view-selected",
8589      /**
8590      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8591      */
8592     emptyText : "",
8593     
8594     /**
8595      * @cfg {String} text to display on mask (default Loading)
8596      */
8597     mask : false,
8598     /**
8599      * @cfg {Boolean} multiSelect Allow multiple selection
8600      */
8601     multiSelect : false,
8602     /**
8603      * @cfg {Boolean} singleSelect Allow single selection
8604      */
8605     singleSelect:  false,
8606     
8607     /**
8608      * @cfg {Boolean} toggleSelect - selecting 
8609      */
8610     toggleSelect : false,
8611     
8612     /**
8613      * @cfg {Boolean} tickable - selecting 
8614      */
8615     tickable : false,
8616     
8617     /**
8618      * Returns the element this view is bound to.
8619      * @return {Roo.Element}
8620      */
8621     getEl : function(){
8622         return this.wrapEl;
8623     },
8624     
8625     
8626
8627     /**
8628      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8629      */
8630     refresh : function(){
8631         //Roo.log('refresh');
8632         var t = this.tpl;
8633         
8634         // if we are using something like 'domtemplate', then
8635         // the what gets used is:
8636         // t.applySubtemplate(NAME, data, wrapping data..)
8637         // the outer template then get' applied with
8638         //     the store 'extra data'
8639         // and the body get's added to the
8640         //      roo-name="data" node?
8641         //      <span class='roo-tpl-{name}'></span> ?????
8642         
8643         
8644         
8645         this.clearSelections();
8646         this.el.update("");
8647         var html = [];
8648         var records = this.store.getRange();
8649         if(records.length < 1) {
8650             
8651             // is this valid??  = should it render a template??
8652             
8653             this.el.update(this.emptyText);
8654             return;
8655         }
8656         var el = this.el;
8657         if (this.dataName) {
8658             this.el.update(t.apply(this.store.meta)); //????
8659             el = this.el.child('.roo-tpl-' + this.dataName);
8660         }
8661         
8662         for(var i = 0, len = records.length; i < len; i++){
8663             var data = this.prepareData(records[i].data, i, records[i]);
8664             this.fireEvent("preparedata", this, data, i, records[i]);
8665             
8666             var d = Roo.apply({}, data);
8667             
8668             if(this.tickable){
8669                 Roo.apply(d, {'roo-id' : Roo.id()});
8670                 
8671                 var _this = this;
8672             
8673                 Roo.each(this.parent.item, function(item){
8674                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8675                         return;
8676                     }
8677                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8678                 });
8679             }
8680             
8681             html[html.length] = Roo.util.Format.trim(
8682                 this.dataName ?
8683                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8684                     t.apply(d)
8685             );
8686         }
8687         
8688         
8689         
8690         el.update(html.join(""));
8691         this.nodes = el.dom.childNodes;
8692         this.updateIndexes(0);
8693     },
8694     
8695
8696     /**
8697      * Function to override to reformat the data that is sent to
8698      * the template for each node.
8699      * DEPRICATED - use the preparedata event handler.
8700      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8701      * a JSON object for an UpdateManager bound view).
8702      */
8703     prepareData : function(data, index, record)
8704     {
8705         this.fireEvent("preparedata", this, data, index, record);
8706         return data;
8707     },
8708
8709     onUpdate : function(ds, record){
8710         // Roo.log('on update');   
8711         this.clearSelections();
8712         var index = this.store.indexOf(record);
8713         var n = this.nodes[index];
8714         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8715         n.parentNode.removeChild(n);
8716         this.updateIndexes(index, index);
8717     },
8718
8719     
8720     
8721 // --------- FIXME     
8722     onAdd : function(ds, records, index)
8723     {
8724         //Roo.log(['on Add', ds, records, index] );        
8725         this.clearSelections();
8726         if(this.nodes.length == 0){
8727             this.refresh();
8728             return;
8729         }
8730         var n = this.nodes[index];
8731         for(var i = 0, len = records.length; i < len; i++){
8732             var d = this.prepareData(records[i].data, i, records[i]);
8733             if(n){
8734                 this.tpl.insertBefore(n, d);
8735             }else{
8736                 
8737                 this.tpl.append(this.el, d);
8738             }
8739         }
8740         this.updateIndexes(index);
8741     },
8742
8743     onRemove : function(ds, record, index){
8744        // Roo.log('onRemove');
8745         this.clearSelections();
8746         var el = this.dataName  ?
8747             this.el.child('.roo-tpl-' + this.dataName) :
8748             this.el; 
8749         
8750         el.dom.removeChild(this.nodes[index]);
8751         this.updateIndexes(index);
8752     },
8753
8754     /**
8755      * Refresh an individual node.
8756      * @param {Number} index
8757      */
8758     refreshNode : function(index){
8759         this.onUpdate(this.store, this.store.getAt(index));
8760     },
8761
8762     updateIndexes : function(startIndex, endIndex){
8763         var ns = this.nodes;
8764         startIndex = startIndex || 0;
8765         endIndex = endIndex || ns.length - 1;
8766         for(var i = startIndex; i <= endIndex; i++){
8767             ns[i].nodeIndex = i;
8768         }
8769     },
8770
8771     /**
8772      * Changes the data store this view uses and refresh the view.
8773      * @param {Store} store
8774      */
8775     setStore : function(store, initial){
8776         if(!initial && this.store){
8777             this.store.un("datachanged", this.refresh);
8778             this.store.un("add", this.onAdd);
8779             this.store.un("remove", this.onRemove);
8780             this.store.un("update", this.onUpdate);
8781             this.store.un("clear", this.refresh);
8782             this.store.un("beforeload", this.onBeforeLoad);
8783             this.store.un("load", this.onLoad);
8784             this.store.un("loadexception", this.onLoad);
8785         }
8786         if(store){
8787           
8788             store.on("datachanged", this.refresh, this);
8789             store.on("add", this.onAdd, this);
8790             store.on("remove", this.onRemove, this);
8791             store.on("update", this.onUpdate, this);
8792             store.on("clear", this.refresh, this);
8793             store.on("beforeload", this.onBeforeLoad, this);
8794             store.on("load", this.onLoad, this);
8795             store.on("loadexception", this.onLoad, this);
8796         }
8797         
8798         if(store){
8799             this.refresh();
8800         }
8801     },
8802     /**
8803      * onbeforeLoad - masks the loading area.
8804      *
8805      */
8806     onBeforeLoad : function(store,opts)
8807     {
8808          //Roo.log('onBeforeLoad');   
8809         if (!opts.add) {
8810             this.el.update("");
8811         }
8812         this.el.mask(this.mask ? this.mask : "Loading" ); 
8813     },
8814     onLoad : function ()
8815     {
8816         this.el.unmask();
8817     },
8818     
8819
8820     /**
8821      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8822      * @param {HTMLElement} node
8823      * @return {HTMLElement} The template node
8824      */
8825     findItemFromChild : function(node){
8826         var el = this.dataName  ?
8827             this.el.child('.roo-tpl-' + this.dataName,true) :
8828             this.el.dom; 
8829         
8830         if(!node || node.parentNode == el){
8831                     return node;
8832             }
8833             var p = node.parentNode;
8834             while(p && p != el){
8835             if(p.parentNode == el){
8836                 return p;
8837             }
8838             p = p.parentNode;
8839         }
8840             return null;
8841     },
8842
8843     /** @ignore */
8844     onClick : function(e){
8845         var item = this.findItemFromChild(e.getTarget());
8846         if(item){
8847             var index = this.indexOf(item);
8848             if(this.onItemClick(item, index, e) !== false){
8849                 this.fireEvent("click", this, index, item, e);
8850             }
8851         }else{
8852             this.clearSelections();
8853         }
8854     },
8855
8856     /** @ignore */
8857     onContextMenu : function(e){
8858         var item = this.findItemFromChild(e.getTarget());
8859         if(item){
8860             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8861         }
8862     },
8863
8864     /** @ignore */
8865     onDblClick : function(e){
8866         var item = this.findItemFromChild(e.getTarget());
8867         if(item){
8868             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8869         }
8870     },
8871
8872     onItemClick : function(item, index, e)
8873     {
8874         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8875             return false;
8876         }
8877         if (this.toggleSelect) {
8878             var m = this.isSelected(item) ? 'unselect' : 'select';
8879             //Roo.log(m);
8880             var _t = this;
8881             _t[m](item, true, false);
8882             return true;
8883         }
8884         if(this.multiSelect || this.singleSelect){
8885             if(this.multiSelect && e.shiftKey && this.lastSelection){
8886                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8887             }else{
8888                 this.select(item, this.multiSelect && e.ctrlKey);
8889                 this.lastSelection = item;
8890             }
8891             
8892             if(!this.tickable){
8893                 e.preventDefault();
8894             }
8895             
8896         }
8897         return true;
8898     },
8899
8900     /**
8901      * Get the number of selected nodes.
8902      * @return {Number}
8903      */
8904     getSelectionCount : function(){
8905         return this.selections.length;
8906     },
8907
8908     /**
8909      * Get the currently selected nodes.
8910      * @return {Array} An array of HTMLElements
8911      */
8912     getSelectedNodes : function(){
8913         return this.selections;
8914     },
8915
8916     /**
8917      * Get the indexes of the selected nodes.
8918      * @return {Array}
8919      */
8920     getSelectedIndexes : function(){
8921         var indexes = [], s = this.selections;
8922         for(var i = 0, len = s.length; i < len; i++){
8923             indexes.push(s[i].nodeIndex);
8924         }
8925         return indexes;
8926     },
8927
8928     /**
8929      * Clear all selections
8930      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8931      */
8932     clearSelections : function(suppressEvent){
8933         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8934             this.cmp.elements = this.selections;
8935             this.cmp.removeClass(this.selectedClass);
8936             this.selections = [];
8937             if(!suppressEvent){
8938                 this.fireEvent("selectionchange", this, this.selections);
8939             }
8940         }
8941     },
8942
8943     /**
8944      * Returns true if the passed node is selected
8945      * @param {HTMLElement/Number} node The node or node index
8946      * @return {Boolean}
8947      */
8948     isSelected : function(node){
8949         var s = this.selections;
8950         if(s.length < 1){
8951             return false;
8952         }
8953         node = this.getNode(node);
8954         return s.indexOf(node) !== -1;
8955     },
8956
8957     /**
8958      * Selects nodes.
8959      * @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
8960      * @param {Boolean} keepExisting (optional) true to keep existing selections
8961      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8962      */
8963     select : function(nodeInfo, keepExisting, suppressEvent){
8964         if(nodeInfo instanceof Array){
8965             if(!keepExisting){
8966                 this.clearSelections(true);
8967             }
8968             for(var i = 0, len = nodeInfo.length; i < len; i++){
8969                 this.select(nodeInfo[i], true, true);
8970             }
8971             return;
8972         } 
8973         var node = this.getNode(nodeInfo);
8974         if(!node || this.isSelected(node)){
8975             return; // already selected.
8976         }
8977         if(!keepExisting){
8978             this.clearSelections(true);
8979         }
8980         
8981         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8982             Roo.fly(node).addClass(this.selectedClass);
8983             this.selections.push(node);
8984             if(!suppressEvent){
8985                 this.fireEvent("selectionchange", this, this.selections);
8986             }
8987         }
8988         
8989         
8990     },
8991       /**
8992      * Unselects nodes.
8993      * @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
8994      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8995      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8996      */
8997     unselect : function(nodeInfo, keepExisting, suppressEvent)
8998     {
8999         if(nodeInfo instanceof Array){
9000             Roo.each(this.selections, function(s) {
9001                 this.unselect(s, nodeInfo);
9002             }, this);
9003             return;
9004         }
9005         var node = this.getNode(nodeInfo);
9006         if(!node || !this.isSelected(node)){
9007             //Roo.log("not selected");
9008             return; // not selected.
9009         }
9010         // fireevent???
9011         var ns = [];
9012         Roo.each(this.selections, function(s) {
9013             if (s == node ) {
9014                 Roo.fly(node).removeClass(this.selectedClass);
9015
9016                 return;
9017             }
9018             ns.push(s);
9019         },this);
9020         
9021         this.selections= ns;
9022         this.fireEvent("selectionchange", this, this.selections);
9023     },
9024
9025     /**
9026      * Gets a template node.
9027      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9028      * @return {HTMLElement} The node or null if it wasn't found
9029      */
9030     getNode : function(nodeInfo){
9031         if(typeof nodeInfo == "string"){
9032             return document.getElementById(nodeInfo);
9033         }else if(typeof nodeInfo == "number"){
9034             return this.nodes[nodeInfo];
9035         }
9036         return nodeInfo;
9037     },
9038
9039     /**
9040      * Gets a range template nodes.
9041      * @param {Number} startIndex
9042      * @param {Number} endIndex
9043      * @return {Array} An array of nodes
9044      */
9045     getNodes : function(start, end){
9046         var ns = this.nodes;
9047         start = start || 0;
9048         end = typeof end == "undefined" ? ns.length - 1 : end;
9049         var nodes = [];
9050         if(start <= end){
9051             for(var i = start; i <= end; i++){
9052                 nodes.push(ns[i]);
9053             }
9054         } else{
9055             for(var i = start; i >= end; i--){
9056                 nodes.push(ns[i]);
9057             }
9058         }
9059         return nodes;
9060     },
9061
9062     /**
9063      * Finds the index of the passed node
9064      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9065      * @return {Number} The index of the node or -1
9066      */
9067     indexOf : function(node){
9068         node = this.getNode(node);
9069         if(typeof node.nodeIndex == "number"){
9070             return node.nodeIndex;
9071         }
9072         var ns = this.nodes;
9073         for(var i = 0, len = ns.length; i < len; i++){
9074             if(ns[i] == node){
9075                 return i;
9076             }
9077         }
9078         return -1;
9079     }
9080 });
9081 /*
9082  * Based on:
9083  * Ext JS Library 1.1.1
9084  * Copyright(c) 2006-2007, Ext JS, LLC.
9085  *
9086  * Originally Released Under LGPL - original licence link has changed is not relivant.
9087  *
9088  * Fork - LGPL
9089  * <script type="text/javascript">
9090  */
9091
9092 /**
9093  * @class Roo.JsonView
9094  * @extends Roo.View
9095  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9096 <pre><code>
9097 var view = new Roo.JsonView({
9098     container: "my-element",
9099     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9100     multiSelect: true, 
9101     jsonRoot: "data" 
9102 });
9103
9104 // listen for node click?
9105 view.on("click", function(vw, index, node, e){
9106     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9107 });
9108
9109 // direct load of JSON data
9110 view.load("foobar.php");
9111
9112 // Example from my blog list
9113 var tpl = new Roo.Template(
9114     '&lt;div class="entry"&gt;' +
9115     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9116     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9117     "&lt;/div&gt;&lt;hr /&gt;"
9118 );
9119
9120 var moreView = new Roo.JsonView({
9121     container :  "entry-list", 
9122     template : tpl,
9123     jsonRoot: "posts"
9124 });
9125 moreView.on("beforerender", this.sortEntries, this);
9126 moreView.load({
9127     url: "/blog/get-posts.php",
9128     params: "allposts=true",
9129     text: "Loading Blog Entries..."
9130 });
9131 </code></pre>
9132
9133 * Note: old code is supported with arguments : (container, template, config)
9134
9135
9136  * @constructor
9137  * Create a new JsonView
9138  * 
9139  * @param {Object} config The config object
9140  * 
9141  */
9142 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9143     
9144     
9145     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9146
9147     var um = this.el.getUpdateManager();
9148     um.setRenderer(this);
9149     um.on("update", this.onLoad, this);
9150     um.on("failure", this.onLoadException, this);
9151
9152     /**
9153      * @event beforerender
9154      * Fires before rendering of the downloaded JSON data.
9155      * @param {Roo.JsonView} this
9156      * @param {Object} data The JSON data loaded
9157      */
9158     /**
9159      * @event load
9160      * Fires when data is loaded.
9161      * @param {Roo.JsonView} this
9162      * @param {Object} data The JSON data loaded
9163      * @param {Object} response The raw Connect response object
9164      */
9165     /**
9166      * @event loadexception
9167      * Fires when loading fails.
9168      * @param {Roo.JsonView} this
9169      * @param {Object} response The raw Connect response object
9170      */
9171     this.addEvents({
9172         'beforerender' : true,
9173         'load' : true,
9174         'loadexception' : true
9175     });
9176 };
9177 Roo.extend(Roo.JsonView, Roo.View, {
9178     /**
9179      * @type {String} The root property in the loaded JSON object that contains the data
9180      */
9181     jsonRoot : "",
9182
9183     /**
9184      * Refreshes the view.
9185      */
9186     refresh : function(){
9187         this.clearSelections();
9188         this.el.update("");
9189         var html = [];
9190         var o = this.jsonData;
9191         if(o && o.length > 0){
9192             for(var i = 0, len = o.length; i < len; i++){
9193                 var data = this.prepareData(o[i], i, o);
9194                 html[html.length] = this.tpl.apply(data);
9195             }
9196         }else{
9197             html.push(this.emptyText);
9198         }
9199         this.el.update(html.join(""));
9200         this.nodes = this.el.dom.childNodes;
9201         this.updateIndexes(0);
9202     },
9203
9204     /**
9205      * 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.
9206      * @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:
9207      <pre><code>
9208      view.load({
9209          url: "your-url.php",
9210          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9211          callback: yourFunction,
9212          scope: yourObject, //(optional scope)
9213          discardUrl: false,
9214          nocache: false,
9215          text: "Loading...",
9216          timeout: 30,
9217          scripts: false
9218      });
9219      </code></pre>
9220      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9221      * 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.
9222      * @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}
9223      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9224      * @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.
9225      */
9226     load : function(){
9227         var um = this.el.getUpdateManager();
9228         um.update.apply(um, arguments);
9229     },
9230
9231     render : function(el, response){
9232         this.clearSelections();
9233         this.el.update("");
9234         var o;
9235         try{
9236             o = Roo.util.JSON.decode(response.responseText);
9237             if(this.jsonRoot){
9238                 
9239                 o = o[this.jsonRoot];
9240             }
9241         } catch(e){
9242         }
9243         /**
9244          * The current JSON data or null
9245          */
9246         this.jsonData = o;
9247         this.beforeRender();
9248         this.refresh();
9249     },
9250
9251 /**
9252  * Get the number of records in the current JSON dataset
9253  * @return {Number}
9254  */
9255     getCount : function(){
9256         return this.jsonData ? this.jsonData.length : 0;
9257     },
9258
9259 /**
9260  * Returns the JSON object for the specified node(s)
9261  * @param {HTMLElement/Array} node The node or an array of nodes
9262  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9263  * you get the JSON object for the node
9264  */
9265     getNodeData : function(node){
9266         if(node instanceof Array){
9267             var data = [];
9268             for(var i = 0, len = node.length; i < len; i++){
9269                 data.push(this.getNodeData(node[i]));
9270             }
9271             return data;
9272         }
9273         return this.jsonData[this.indexOf(node)] || null;
9274     },
9275
9276     beforeRender : function(){
9277         this.snapshot = this.jsonData;
9278         if(this.sortInfo){
9279             this.sort.apply(this, this.sortInfo);
9280         }
9281         this.fireEvent("beforerender", this, this.jsonData);
9282     },
9283
9284     onLoad : function(el, o){
9285         this.fireEvent("load", this, this.jsonData, o);
9286     },
9287
9288     onLoadException : function(el, o){
9289         this.fireEvent("loadexception", this, o);
9290     },
9291
9292 /**
9293  * Filter the data by a specific property.
9294  * @param {String} property A property on your JSON objects
9295  * @param {String/RegExp} value Either string that the property values
9296  * should start with, or a RegExp to test against the property
9297  */
9298     filter : function(property, value){
9299         if(this.jsonData){
9300             var data = [];
9301             var ss = this.snapshot;
9302             if(typeof value == "string"){
9303                 var vlen = value.length;
9304                 if(vlen == 0){
9305                     this.clearFilter();
9306                     return;
9307                 }
9308                 value = value.toLowerCase();
9309                 for(var i = 0, len = ss.length; i < len; i++){
9310                     var o = ss[i];
9311                     if(o[property].substr(0, vlen).toLowerCase() == value){
9312                         data.push(o);
9313                     }
9314                 }
9315             } else if(value.exec){ // regex?
9316                 for(var i = 0, len = ss.length; i < len; i++){
9317                     var o = ss[i];
9318                     if(value.test(o[property])){
9319                         data.push(o);
9320                     }
9321                 }
9322             } else{
9323                 return;
9324             }
9325             this.jsonData = data;
9326             this.refresh();
9327         }
9328     },
9329
9330 /**
9331  * Filter by a function. The passed function will be called with each
9332  * object in the current dataset. If the function returns true the value is kept,
9333  * otherwise it is filtered.
9334  * @param {Function} fn
9335  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9336  */
9337     filterBy : function(fn, scope){
9338         if(this.jsonData){
9339             var data = [];
9340             var ss = this.snapshot;
9341             for(var i = 0, len = ss.length; i < len; i++){
9342                 var o = ss[i];
9343                 if(fn.call(scope || this, o)){
9344                     data.push(o);
9345                 }
9346             }
9347             this.jsonData = data;
9348             this.refresh();
9349         }
9350     },
9351
9352 /**
9353  * Clears the current filter.
9354  */
9355     clearFilter : function(){
9356         if(this.snapshot && this.jsonData != this.snapshot){
9357             this.jsonData = this.snapshot;
9358             this.refresh();
9359         }
9360     },
9361
9362
9363 /**
9364  * Sorts the data for this view and refreshes it.
9365  * @param {String} property A property on your JSON objects to sort on
9366  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9367  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9368  */
9369     sort : function(property, dir, sortType){
9370         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9371         if(this.jsonData){
9372             var p = property;
9373             var dsc = dir && dir.toLowerCase() == "desc";
9374             var f = function(o1, o2){
9375                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9376                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9377                 ;
9378                 if(v1 < v2){
9379                     return dsc ? +1 : -1;
9380                 } else if(v1 > v2){
9381                     return dsc ? -1 : +1;
9382                 } else{
9383                     return 0;
9384                 }
9385             };
9386             this.jsonData.sort(f);
9387             this.refresh();
9388             if(this.jsonData != this.snapshot){
9389                 this.snapshot.sort(f);
9390             }
9391         }
9392     }
9393 });/*
9394  * Based on:
9395  * Ext JS Library 1.1.1
9396  * Copyright(c) 2006-2007, Ext JS, LLC.
9397  *
9398  * Originally Released Under LGPL - original licence link has changed is not relivant.
9399  *
9400  * Fork - LGPL
9401  * <script type="text/javascript">
9402  */
9403  
9404
9405 /**
9406  * @class Roo.ColorPalette
9407  * @extends Roo.Component
9408  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9409  * Here's an example of typical usage:
9410  * <pre><code>
9411 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9412 cp.render('my-div');
9413
9414 cp.on('select', function(palette, selColor){
9415     // do something with selColor
9416 });
9417 </code></pre>
9418  * @constructor
9419  * Create a new ColorPalette
9420  * @param {Object} config The config object
9421  */
9422 Roo.ColorPalette = function(config){
9423     Roo.ColorPalette.superclass.constructor.call(this, config);
9424     this.addEvents({
9425         /**
9426              * @event select
9427              * Fires when a color is selected
9428              * @param {ColorPalette} this
9429              * @param {String} color The 6-digit color hex code (without the # symbol)
9430              */
9431         select: true
9432     });
9433
9434     if(this.handler){
9435         this.on("select", this.handler, this.scope, true);
9436     }
9437 };
9438 Roo.extend(Roo.ColorPalette, Roo.Component, {
9439     /**
9440      * @cfg {String} itemCls
9441      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9442      */
9443     itemCls : "x-color-palette",
9444     /**
9445      * @cfg {String} value
9446      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9447      * the hex codes are case-sensitive.
9448      */
9449     value : null,
9450     clickEvent:'click',
9451     // private
9452     ctype: "Roo.ColorPalette",
9453
9454     /**
9455      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9456      */
9457     allowReselect : false,
9458
9459     /**
9460      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9461      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9462      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9463      * of colors with the width setting until the box is symmetrical.</p>
9464      * <p>You can override individual colors if needed:</p>
9465      * <pre><code>
9466 var cp = new Roo.ColorPalette();
9467 cp.colors[0] = "FF0000";  // change the first box to red
9468 </code></pre>
9469
9470 Or you can provide a custom array of your own for complete control:
9471 <pre><code>
9472 var cp = new Roo.ColorPalette();
9473 cp.colors = ["000000", "993300", "333300"];
9474 </code></pre>
9475      * @type Array
9476      */
9477     colors : [
9478         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9479         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9480         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9481         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9482         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9483     ],
9484
9485     // private
9486     onRender : function(container, position){
9487         var t = new Roo.MasterTemplate(
9488             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9489         );
9490         var c = this.colors;
9491         for(var i = 0, len = c.length; i < len; i++){
9492             t.add([c[i]]);
9493         }
9494         var el = document.createElement("div");
9495         el.className = this.itemCls;
9496         t.overwrite(el);
9497         container.dom.insertBefore(el, position);
9498         this.el = Roo.get(el);
9499         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9500         if(this.clickEvent != 'click'){
9501             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9502         }
9503     },
9504
9505     // private
9506     afterRender : function(){
9507         Roo.ColorPalette.superclass.afterRender.call(this);
9508         if(this.value){
9509             var s = this.value;
9510             this.value = null;
9511             this.select(s);
9512         }
9513     },
9514
9515     // private
9516     handleClick : function(e, t){
9517         e.preventDefault();
9518         if(!this.disabled){
9519             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9520             this.select(c.toUpperCase());
9521         }
9522     },
9523
9524     /**
9525      * Selects the specified color in the palette (fires the select event)
9526      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9527      */
9528     select : function(color){
9529         color = color.replace("#", "");
9530         if(color != this.value || this.allowReselect){
9531             var el = this.el;
9532             if(this.value){
9533                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9534             }
9535             el.child("a.color-"+color).addClass("x-color-palette-sel");
9536             this.value = color;
9537             this.fireEvent("select", this, color);
9538         }
9539     }
9540 });/*
9541  * Based on:
9542  * Ext JS Library 1.1.1
9543  * Copyright(c) 2006-2007, Ext JS, LLC.
9544  *
9545  * Originally Released Under LGPL - original licence link has changed is not relivant.
9546  *
9547  * Fork - LGPL
9548  * <script type="text/javascript">
9549  */
9550  
9551 /**
9552  * @class Roo.DatePicker
9553  * @extends Roo.Component
9554  * Simple date picker class.
9555  * @constructor
9556  * Create a new DatePicker
9557  * @param {Object} config The config object
9558  */
9559 Roo.DatePicker = function(config){
9560     Roo.DatePicker.superclass.constructor.call(this, config);
9561
9562     this.value = config && config.value ?
9563                  config.value.clearTime() : new Date().clearTime();
9564
9565     this.addEvents({
9566         /**
9567              * @event select
9568              * Fires when a date is selected
9569              * @param {DatePicker} this
9570              * @param {Date} date The selected date
9571              */
9572         'select': true,
9573         /**
9574              * @event monthchange
9575              * Fires when the displayed month changes 
9576              * @param {DatePicker} this
9577              * @param {Date} date The selected month
9578              */
9579         'monthchange': true
9580     });
9581
9582     if(this.handler){
9583         this.on("select", this.handler,  this.scope || this);
9584     }
9585     // build the disabledDatesRE
9586     if(!this.disabledDatesRE && this.disabledDates){
9587         var dd = this.disabledDates;
9588         var re = "(?:";
9589         for(var i = 0; i < dd.length; i++){
9590             re += dd[i];
9591             if(i != dd.length-1) {
9592                 re += "|";
9593             }
9594         }
9595         this.disabledDatesRE = new RegExp(re + ")");
9596     }
9597 };
9598
9599 Roo.extend(Roo.DatePicker, Roo.Component, {
9600     /**
9601      * @cfg {String} todayText
9602      * The text to display on the button that selects the current date (defaults to "Today")
9603      */
9604     todayText : "Today",
9605     /**
9606      * @cfg {String} okText
9607      * The text to display on the ok button
9608      */
9609     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9610     /**
9611      * @cfg {String} cancelText
9612      * The text to display on the cancel button
9613      */
9614     cancelText : "Cancel",
9615     /**
9616      * @cfg {String} todayTip
9617      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9618      */
9619     todayTip : "{0} (Spacebar)",
9620     /**
9621      * @cfg {Date} minDate
9622      * Minimum allowable date (JavaScript date object, defaults to null)
9623      */
9624     minDate : null,
9625     /**
9626      * @cfg {Date} maxDate
9627      * Maximum allowable date (JavaScript date object, defaults to null)
9628      */
9629     maxDate : null,
9630     /**
9631      * @cfg {String} minText
9632      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9633      */
9634     minText : "This date is before the minimum date",
9635     /**
9636      * @cfg {String} maxText
9637      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9638      */
9639     maxText : "This date is after the maximum date",
9640     /**
9641      * @cfg {String} format
9642      * The default date format string which can be overriden for localization support.  The format must be
9643      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9644      */
9645     format : "m/d/y",
9646     /**
9647      * @cfg {Array} disabledDays
9648      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9649      */
9650     disabledDays : null,
9651     /**
9652      * @cfg {String} disabledDaysText
9653      * The tooltip to display when the date falls on a disabled day (defaults to "")
9654      */
9655     disabledDaysText : "",
9656     /**
9657      * @cfg {RegExp} disabledDatesRE
9658      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9659      */
9660     disabledDatesRE : null,
9661     /**
9662      * @cfg {String} disabledDatesText
9663      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9664      */
9665     disabledDatesText : "",
9666     /**
9667      * @cfg {Boolean} constrainToViewport
9668      * True to constrain the date picker to the viewport (defaults to true)
9669      */
9670     constrainToViewport : true,
9671     /**
9672      * @cfg {Array} monthNames
9673      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9674      */
9675     monthNames : Date.monthNames,
9676     /**
9677      * @cfg {Array} dayNames
9678      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9679      */
9680     dayNames : Date.dayNames,
9681     /**
9682      * @cfg {String} nextText
9683      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9684      */
9685     nextText: 'Next Month (Control+Right)',
9686     /**
9687      * @cfg {String} prevText
9688      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9689      */
9690     prevText: 'Previous Month (Control+Left)',
9691     /**
9692      * @cfg {String} monthYearText
9693      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9694      */
9695     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9696     /**
9697      * @cfg {Number} startDay
9698      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9699      */
9700     startDay : 0,
9701     /**
9702      * @cfg {Bool} showClear
9703      * Show a clear button (usefull for date form elements that can be blank.)
9704      */
9705     
9706     showClear: false,
9707     
9708     /**
9709      * Sets the value of the date field
9710      * @param {Date} value The date to set
9711      */
9712     setValue : function(value){
9713         var old = this.value;
9714         
9715         if (typeof(value) == 'string') {
9716          
9717             value = Date.parseDate(value, this.format);
9718         }
9719         if (!value) {
9720             value = new Date();
9721         }
9722         
9723         this.value = value.clearTime(true);
9724         if(this.el){
9725             this.update(this.value);
9726         }
9727     },
9728
9729     /**
9730      * Gets the current selected value of the date field
9731      * @return {Date} The selected date
9732      */
9733     getValue : function(){
9734         return this.value;
9735     },
9736
9737     // private
9738     focus : function(){
9739         if(this.el){
9740             this.update(this.activeDate);
9741         }
9742     },
9743
9744     // privateval
9745     onRender : function(container, position){
9746         
9747         var m = [
9748              '<table cellspacing="0">',
9749                 '<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>',
9750                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9751         var dn = this.dayNames;
9752         for(var i = 0; i < 7; i++){
9753             var d = this.startDay+i;
9754             if(d > 6){
9755                 d = d-7;
9756             }
9757             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9758         }
9759         m[m.length] = "</tr></thead><tbody><tr>";
9760         for(var i = 0; i < 42; i++) {
9761             if(i % 7 == 0 && i != 0){
9762                 m[m.length] = "</tr><tr>";
9763             }
9764             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9765         }
9766         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9767             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9768
9769         var el = document.createElement("div");
9770         el.className = "x-date-picker";
9771         el.innerHTML = m.join("");
9772
9773         container.dom.insertBefore(el, position);
9774
9775         this.el = Roo.get(el);
9776         this.eventEl = Roo.get(el.firstChild);
9777
9778         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9779             handler: this.showPrevMonth,
9780             scope: this,
9781             preventDefault:true,
9782             stopDefault:true
9783         });
9784
9785         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9786             handler: this.showNextMonth,
9787             scope: this,
9788             preventDefault:true,
9789             stopDefault:true
9790         });
9791
9792         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9793
9794         this.monthPicker = this.el.down('div.x-date-mp');
9795         this.monthPicker.enableDisplayMode('block');
9796         
9797         var kn = new Roo.KeyNav(this.eventEl, {
9798             "left" : function(e){
9799                 e.ctrlKey ?
9800                     this.showPrevMonth() :
9801                     this.update(this.activeDate.add("d", -1));
9802             },
9803
9804             "right" : function(e){
9805                 e.ctrlKey ?
9806                     this.showNextMonth() :
9807                     this.update(this.activeDate.add("d", 1));
9808             },
9809
9810             "up" : function(e){
9811                 e.ctrlKey ?
9812                     this.showNextYear() :
9813                     this.update(this.activeDate.add("d", -7));
9814             },
9815
9816             "down" : function(e){
9817                 e.ctrlKey ?
9818                     this.showPrevYear() :
9819                     this.update(this.activeDate.add("d", 7));
9820             },
9821
9822             "pageUp" : function(e){
9823                 this.showNextMonth();
9824             },
9825
9826             "pageDown" : function(e){
9827                 this.showPrevMonth();
9828             },
9829
9830             "enter" : function(e){
9831                 e.stopPropagation();
9832                 return true;
9833             },
9834
9835             scope : this
9836         });
9837
9838         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9839
9840         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9841
9842         this.el.unselectable();
9843         
9844         this.cells = this.el.select("table.x-date-inner tbody td");
9845         this.textNodes = this.el.query("table.x-date-inner tbody span");
9846
9847         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9848             text: "&#160;",
9849             tooltip: this.monthYearText
9850         });
9851
9852         this.mbtn.on('click', this.showMonthPicker, this);
9853         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9854
9855
9856         var today = (new Date()).dateFormat(this.format);
9857         
9858         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9859         if (this.showClear) {
9860             baseTb.add( new Roo.Toolbar.Fill());
9861         }
9862         baseTb.add({
9863             text: String.format(this.todayText, today),
9864             tooltip: String.format(this.todayTip, today),
9865             handler: this.selectToday,
9866             scope: this
9867         });
9868         
9869         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9870             
9871         //});
9872         if (this.showClear) {
9873             
9874             baseTb.add( new Roo.Toolbar.Fill());
9875             baseTb.add({
9876                 text: '&#160;',
9877                 cls: 'x-btn-icon x-btn-clear',
9878                 handler: function() {
9879                     //this.value = '';
9880                     this.fireEvent("select", this, '');
9881                 },
9882                 scope: this
9883             });
9884         }
9885         
9886         
9887         if(Roo.isIE){
9888             this.el.repaint();
9889         }
9890         this.update(this.value);
9891     },
9892
9893     createMonthPicker : function(){
9894         if(!this.monthPicker.dom.firstChild){
9895             var buf = ['<table border="0" cellspacing="0">'];
9896             for(var i = 0; i < 6; i++){
9897                 buf.push(
9898                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9899                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9900                     i == 0 ?
9901                     '<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>' :
9902                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9903                 );
9904             }
9905             buf.push(
9906                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9907                     this.okText,
9908                     '</button><button type="button" class="x-date-mp-cancel">',
9909                     this.cancelText,
9910                     '</button></td></tr>',
9911                 '</table>'
9912             );
9913             this.monthPicker.update(buf.join(''));
9914             this.monthPicker.on('click', this.onMonthClick, this);
9915             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9916
9917             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9918             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9919
9920             this.mpMonths.each(function(m, a, i){
9921                 i += 1;
9922                 if((i%2) == 0){
9923                     m.dom.xmonth = 5 + Math.round(i * .5);
9924                 }else{
9925                     m.dom.xmonth = Math.round((i-1) * .5);
9926                 }
9927             });
9928         }
9929     },
9930
9931     showMonthPicker : function(){
9932         this.createMonthPicker();
9933         var size = this.el.getSize();
9934         this.monthPicker.setSize(size);
9935         this.monthPicker.child('table').setSize(size);
9936
9937         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9938         this.updateMPMonth(this.mpSelMonth);
9939         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9940         this.updateMPYear(this.mpSelYear);
9941
9942         this.monthPicker.slideIn('t', {duration:.2});
9943     },
9944
9945     updateMPYear : function(y){
9946         this.mpyear = y;
9947         var ys = this.mpYears.elements;
9948         for(var i = 1; i <= 10; i++){
9949             var td = ys[i-1], y2;
9950             if((i%2) == 0){
9951                 y2 = y + Math.round(i * .5);
9952                 td.firstChild.innerHTML = y2;
9953                 td.xyear = y2;
9954             }else{
9955                 y2 = y - (5-Math.round(i * .5));
9956                 td.firstChild.innerHTML = y2;
9957                 td.xyear = y2;
9958             }
9959             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9960         }
9961     },
9962
9963     updateMPMonth : function(sm){
9964         this.mpMonths.each(function(m, a, i){
9965             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9966         });
9967     },
9968
9969     selectMPMonth: function(m){
9970         
9971     },
9972
9973     onMonthClick : function(e, t){
9974         e.stopEvent();
9975         var el = new Roo.Element(t), pn;
9976         if(el.is('button.x-date-mp-cancel')){
9977             this.hideMonthPicker();
9978         }
9979         else if(el.is('button.x-date-mp-ok')){
9980             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9981             this.hideMonthPicker();
9982         }
9983         else if(pn = el.up('td.x-date-mp-month', 2)){
9984             this.mpMonths.removeClass('x-date-mp-sel');
9985             pn.addClass('x-date-mp-sel');
9986             this.mpSelMonth = pn.dom.xmonth;
9987         }
9988         else if(pn = el.up('td.x-date-mp-year', 2)){
9989             this.mpYears.removeClass('x-date-mp-sel');
9990             pn.addClass('x-date-mp-sel');
9991             this.mpSelYear = pn.dom.xyear;
9992         }
9993         else if(el.is('a.x-date-mp-prev')){
9994             this.updateMPYear(this.mpyear-10);
9995         }
9996         else if(el.is('a.x-date-mp-next')){
9997             this.updateMPYear(this.mpyear+10);
9998         }
9999     },
10000
10001     onMonthDblClick : function(e, t){
10002         e.stopEvent();
10003         var el = new Roo.Element(t), pn;
10004         if(pn = el.up('td.x-date-mp-month', 2)){
10005             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10006             this.hideMonthPicker();
10007         }
10008         else if(pn = el.up('td.x-date-mp-year', 2)){
10009             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10010             this.hideMonthPicker();
10011         }
10012     },
10013
10014     hideMonthPicker : function(disableAnim){
10015         if(this.monthPicker){
10016             if(disableAnim === true){
10017                 this.monthPicker.hide();
10018             }else{
10019                 this.monthPicker.slideOut('t', {duration:.2});
10020             }
10021         }
10022     },
10023
10024     // private
10025     showPrevMonth : function(e){
10026         this.update(this.activeDate.add("mo", -1));
10027     },
10028
10029     // private
10030     showNextMonth : function(e){
10031         this.update(this.activeDate.add("mo", 1));
10032     },
10033
10034     // private
10035     showPrevYear : function(){
10036         this.update(this.activeDate.add("y", -1));
10037     },
10038
10039     // private
10040     showNextYear : function(){
10041         this.update(this.activeDate.add("y", 1));
10042     },
10043
10044     // private
10045     handleMouseWheel : function(e){
10046         var delta = e.getWheelDelta();
10047         if(delta > 0){
10048             this.showPrevMonth();
10049             e.stopEvent();
10050         } else if(delta < 0){
10051             this.showNextMonth();
10052             e.stopEvent();
10053         }
10054     },
10055
10056     // private
10057     handleDateClick : function(e, t){
10058         e.stopEvent();
10059         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10060             this.setValue(new Date(t.dateValue));
10061             this.fireEvent("select", this, this.value);
10062         }
10063     },
10064
10065     // private
10066     selectToday : function(){
10067         this.setValue(new Date().clearTime());
10068         this.fireEvent("select", this, this.value);
10069     },
10070
10071     // private
10072     update : function(date)
10073     {
10074         var vd = this.activeDate;
10075         this.activeDate = date;
10076         if(vd && this.el){
10077             var t = date.getTime();
10078             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10079                 this.cells.removeClass("x-date-selected");
10080                 this.cells.each(function(c){
10081                    if(c.dom.firstChild.dateValue == t){
10082                        c.addClass("x-date-selected");
10083                        setTimeout(function(){
10084                             try{c.dom.firstChild.focus();}catch(e){}
10085                        }, 50);
10086                        return false;
10087                    }
10088                 });
10089                 return;
10090             }
10091         }
10092         
10093         var days = date.getDaysInMonth();
10094         var firstOfMonth = date.getFirstDateOfMonth();
10095         var startingPos = firstOfMonth.getDay()-this.startDay;
10096
10097         if(startingPos <= this.startDay){
10098             startingPos += 7;
10099         }
10100
10101         var pm = date.add("mo", -1);
10102         var prevStart = pm.getDaysInMonth()-startingPos;
10103
10104         var cells = this.cells.elements;
10105         var textEls = this.textNodes;
10106         days += startingPos;
10107
10108         // convert everything to numbers so it's fast
10109         var day = 86400000;
10110         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10111         var today = new Date().clearTime().getTime();
10112         var sel = date.clearTime().getTime();
10113         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10114         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10115         var ddMatch = this.disabledDatesRE;
10116         var ddText = this.disabledDatesText;
10117         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10118         var ddaysText = this.disabledDaysText;
10119         var format = this.format;
10120
10121         var setCellClass = function(cal, cell){
10122             cell.title = "";
10123             var t = d.getTime();
10124             cell.firstChild.dateValue = t;
10125             if(t == today){
10126                 cell.className += " x-date-today";
10127                 cell.title = cal.todayText;
10128             }
10129             if(t == sel){
10130                 cell.className += " x-date-selected";
10131                 setTimeout(function(){
10132                     try{cell.firstChild.focus();}catch(e){}
10133                 }, 50);
10134             }
10135             // disabling
10136             if(t < min) {
10137                 cell.className = " x-date-disabled";
10138                 cell.title = cal.minText;
10139                 return;
10140             }
10141             if(t > max) {
10142                 cell.className = " x-date-disabled";
10143                 cell.title = cal.maxText;
10144                 return;
10145             }
10146             if(ddays){
10147                 if(ddays.indexOf(d.getDay()) != -1){
10148                     cell.title = ddaysText;
10149                     cell.className = " x-date-disabled";
10150                 }
10151             }
10152             if(ddMatch && format){
10153                 var fvalue = d.dateFormat(format);
10154                 if(ddMatch.test(fvalue)){
10155                     cell.title = ddText.replace("%0", fvalue);
10156                     cell.className = " x-date-disabled";
10157                 }
10158             }
10159         };
10160
10161         var i = 0;
10162         for(; i < startingPos; i++) {
10163             textEls[i].innerHTML = (++prevStart);
10164             d.setDate(d.getDate()+1);
10165             cells[i].className = "x-date-prevday";
10166             setCellClass(this, cells[i]);
10167         }
10168         for(; i < days; i++){
10169             intDay = i - startingPos + 1;
10170             textEls[i].innerHTML = (intDay);
10171             d.setDate(d.getDate()+1);
10172             cells[i].className = "x-date-active";
10173             setCellClass(this, cells[i]);
10174         }
10175         var extraDays = 0;
10176         for(; i < 42; i++) {
10177              textEls[i].innerHTML = (++extraDays);
10178              d.setDate(d.getDate()+1);
10179              cells[i].className = "x-date-nextday";
10180              setCellClass(this, cells[i]);
10181         }
10182
10183         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10184         this.fireEvent('monthchange', this, date);
10185         
10186         if(!this.internalRender){
10187             var main = this.el.dom.firstChild;
10188             var w = main.offsetWidth;
10189             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10190             Roo.fly(main).setWidth(w);
10191             this.internalRender = true;
10192             // opera does not respect the auto grow header center column
10193             // then, after it gets a width opera refuses to recalculate
10194             // without a second pass
10195             if(Roo.isOpera && !this.secondPass){
10196                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10197                 this.secondPass = true;
10198                 this.update.defer(10, this, [date]);
10199             }
10200         }
10201         
10202         
10203     }
10204 });        /*
10205  * Based on:
10206  * Ext JS Library 1.1.1
10207  * Copyright(c) 2006-2007, Ext JS, LLC.
10208  *
10209  * Originally Released Under LGPL - original licence link has changed is not relivant.
10210  *
10211  * Fork - LGPL
10212  * <script type="text/javascript">
10213  */
10214 /**
10215  * @class Roo.TabPanel
10216  * @extends Roo.util.Observable
10217  * A lightweight tab container.
10218  * <br><br>
10219  * Usage:
10220  * <pre><code>
10221 // basic tabs 1, built from existing content
10222 var tabs = new Roo.TabPanel("tabs1");
10223 tabs.addTab("script", "View Script");
10224 tabs.addTab("markup", "View Markup");
10225 tabs.activate("script");
10226
10227 // more advanced tabs, built from javascript
10228 var jtabs = new Roo.TabPanel("jtabs");
10229 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10230
10231 // set up the UpdateManager
10232 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10233 var updater = tab2.getUpdateManager();
10234 updater.setDefaultUrl("ajax1.htm");
10235 tab2.on('activate', updater.refresh, updater, true);
10236
10237 // Use setUrl for Ajax loading
10238 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10239 tab3.setUrl("ajax2.htm", null, true);
10240
10241 // Disabled tab
10242 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10243 tab4.disable();
10244
10245 jtabs.activate("jtabs-1");
10246  * </code></pre>
10247  * @constructor
10248  * Create a new TabPanel.
10249  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10250  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10251  */
10252 Roo.TabPanel = function(container, config){
10253     /**
10254     * The container element for this TabPanel.
10255     * @type Roo.Element
10256     */
10257     this.el = Roo.get(container, true);
10258     if(config){
10259         if(typeof config == "boolean"){
10260             this.tabPosition = config ? "bottom" : "top";
10261         }else{
10262             Roo.apply(this, config);
10263         }
10264     }
10265     if(this.tabPosition == "bottom"){
10266         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10267         this.el.addClass("x-tabs-bottom");
10268     }
10269     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10270     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10271     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10272     if(Roo.isIE){
10273         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10274     }
10275     if(this.tabPosition != "bottom"){
10276         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10277          * @type Roo.Element
10278          */
10279         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10280         this.el.addClass("x-tabs-top");
10281     }
10282     this.items = [];
10283
10284     this.bodyEl.setStyle("position", "relative");
10285
10286     this.active = null;
10287     this.activateDelegate = this.activate.createDelegate(this);
10288
10289     this.addEvents({
10290         /**
10291          * @event tabchange
10292          * Fires when the active tab changes
10293          * @param {Roo.TabPanel} this
10294          * @param {Roo.TabPanelItem} activePanel The new active tab
10295          */
10296         "tabchange": true,
10297         /**
10298          * @event beforetabchange
10299          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10300          * @param {Roo.TabPanel} this
10301          * @param {Object} e Set cancel to true on this object to cancel the tab change
10302          * @param {Roo.TabPanelItem} tab The tab being changed to
10303          */
10304         "beforetabchange" : true
10305     });
10306
10307     Roo.EventManager.onWindowResize(this.onResize, this);
10308     this.cpad = this.el.getPadding("lr");
10309     this.hiddenCount = 0;
10310
10311
10312     // toolbar on the tabbar support...
10313     if (this.toolbar) {
10314         var tcfg = this.toolbar;
10315         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10316         this.toolbar = new Roo.Toolbar(tcfg);
10317         if (Roo.isSafari) {
10318             var tbl = tcfg.container.child('table', true);
10319             tbl.setAttribute('width', '100%');
10320         }
10321         
10322     }
10323    
10324
10325
10326     Roo.TabPanel.superclass.constructor.call(this);
10327 };
10328
10329 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10330     /*
10331      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10332      */
10333     tabPosition : "top",
10334     /*
10335      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10336      */
10337     currentTabWidth : 0,
10338     /*
10339      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10340      */
10341     minTabWidth : 40,
10342     /*
10343      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10344      */
10345     maxTabWidth : 250,
10346     /*
10347      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10348      */
10349     preferredTabWidth : 175,
10350     /*
10351      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10352      */
10353     resizeTabs : false,
10354     /*
10355      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10356      */
10357     monitorResize : true,
10358     /*
10359      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10360      */
10361     toolbar : false,
10362
10363     /**
10364      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10365      * @param {String} id The id of the div to use <b>or create</b>
10366      * @param {String} text The text for the tab
10367      * @param {String} content (optional) Content to put in the TabPanelItem body
10368      * @param {Boolean} closable (optional) True to create a close icon on the tab
10369      * @return {Roo.TabPanelItem} The created TabPanelItem
10370      */
10371     addTab : function(id, text, content, closable){
10372         var item = new Roo.TabPanelItem(this, id, text, closable);
10373         this.addTabItem(item);
10374         if(content){
10375             item.setContent(content);
10376         }
10377         return item;
10378     },
10379
10380     /**
10381      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10382      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10383      * @return {Roo.TabPanelItem}
10384      */
10385     getTab : function(id){
10386         return this.items[id];
10387     },
10388
10389     /**
10390      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10391      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10392      */
10393     hideTab : function(id){
10394         var t = this.items[id];
10395         if(!t.isHidden()){
10396            t.setHidden(true);
10397            this.hiddenCount++;
10398            this.autoSizeTabs();
10399         }
10400     },
10401
10402     /**
10403      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10404      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10405      */
10406     unhideTab : function(id){
10407         var t = this.items[id];
10408         if(t.isHidden()){
10409            t.setHidden(false);
10410            this.hiddenCount--;
10411            this.autoSizeTabs();
10412         }
10413     },
10414
10415     /**
10416      * Adds an existing {@link Roo.TabPanelItem}.
10417      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10418      */
10419     addTabItem : function(item){
10420         this.items[item.id] = item;
10421         this.items.push(item);
10422         if(this.resizeTabs){
10423            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10424            this.autoSizeTabs();
10425         }else{
10426             item.autoSize();
10427         }
10428     },
10429
10430     /**
10431      * Removes a {@link Roo.TabPanelItem}.
10432      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10433      */
10434     removeTab : function(id){
10435         var items = this.items;
10436         var tab = items[id];
10437         if(!tab) { return; }
10438         var index = items.indexOf(tab);
10439         if(this.active == tab && items.length > 1){
10440             var newTab = this.getNextAvailable(index);
10441             if(newTab) {
10442                 newTab.activate();
10443             }
10444         }
10445         this.stripEl.dom.removeChild(tab.pnode.dom);
10446         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10447             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10448         }
10449         items.splice(index, 1);
10450         delete this.items[tab.id];
10451         tab.fireEvent("close", tab);
10452         tab.purgeListeners();
10453         this.autoSizeTabs();
10454     },
10455
10456     getNextAvailable : function(start){
10457         var items = this.items;
10458         var index = start;
10459         // look for a next tab that will slide over to
10460         // replace the one being removed
10461         while(index < items.length){
10462             var item = items[++index];
10463             if(item && !item.isHidden()){
10464                 return item;
10465             }
10466         }
10467         // if one isn't found select the previous tab (on the left)
10468         index = start;
10469         while(index >= 0){
10470             var item = items[--index];
10471             if(item && !item.isHidden()){
10472                 return item;
10473             }
10474         }
10475         return null;
10476     },
10477
10478     /**
10479      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10480      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10481      */
10482     disableTab : function(id){
10483         var tab = this.items[id];
10484         if(tab && this.active != tab){
10485             tab.disable();
10486         }
10487     },
10488
10489     /**
10490      * Enables a {@link Roo.TabPanelItem} that is disabled.
10491      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10492      */
10493     enableTab : function(id){
10494         var tab = this.items[id];
10495         tab.enable();
10496     },
10497
10498     /**
10499      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10500      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10501      * @return {Roo.TabPanelItem} The TabPanelItem.
10502      */
10503     activate : function(id){
10504         var tab = this.items[id];
10505         if(!tab){
10506             return null;
10507         }
10508         if(tab == this.active || tab.disabled){
10509             return tab;
10510         }
10511         var e = {};
10512         this.fireEvent("beforetabchange", this, e, tab);
10513         if(e.cancel !== true && !tab.disabled){
10514             if(this.active){
10515                 this.active.hide();
10516             }
10517             this.active = this.items[id];
10518             this.active.show();
10519             this.fireEvent("tabchange", this, this.active);
10520         }
10521         return tab;
10522     },
10523
10524     /**
10525      * Gets the active {@link Roo.TabPanelItem}.
10526      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10527      */
10528     getActiveTab : function(){
10529         return this.active;
10530     },
10531
10532     /**
10533      * Updates the tab body element to fit the height of the container element
10534      * for overflow scrolling
10535      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10536      */
10537     syncHeight : function(targetHeight){
10538         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10539         var bm = this.bodyEl.getMargins();
10540         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10541         this.bodyEl.setHeight(newHeight);
10542         return newHeight;
10543     },
10544
10545     onResize : function(){
10546         if(this.monitorResize){
10547             this.autoSizeTabs();
10548         }
10549     },
10550
10551     /**
10552      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10553      */
10554     beginUpdate : function(){
10555         this.updating = true;
10556     },
10557
10558     /**
10559      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10560      */
10561     endUpdate : function(){
10562         this.updating = false;
10563         this.autoSizeTabs();
10564     },
10565
10566     /**
10567      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10568      */
10569     autoSizeTabs : function(){
10570         var count = this.items.length;
10571         var vcount = count - this.hiddenCount;
10572         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
10573             return;
10574         }
10575         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10576         var availWidth = Math.floor(w / vcount);
10577         var b = this.stripBody;
10578         if(b.getWidth() > w){
10579             var tabs = this.items;
10580             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10581             if(availWidth < this.minTabWidth){
10582                 /*if(!this.sleft){    // incomplete scrolling code
10583                     this.createScrollButtons();
10584                 }
10585                 this.showScroll();
10586                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10587             }
10588         }else{
10589             if(this.currentTabWidth < this.preferredTabWidth){
10590                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10591             }
10592         }
10593     },
10594
10595     /**
10596      * Returns the number of tabs in this TabPanel.
10597      * @return {Number}
10598      */
10599      getCount : function(){
10600          return this.items.length;
10601      },
10602
10603     /**
10604      * Resizes all the tabs to the passed width
10605      * @param {Number} The new width
10606      */
10607     setTabWidth : function(width){
10608         this.currentTabWidth = width;
10609         for(var i = 0, len = this.items.length; i < len; i++) {
10610                 if(!this.items[i].isHidden()) {
10611                 this.items[i].setWidth(width);
10612             }
10613         }
10614     },
10615
10616     /**
10617      * Destroys this TabPanel
10618      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10619      */
10620     destroy : function(removeEl){
10621         Roo.EventManager.removeResizeListener(this.onResize, this);
10622         for(var i = 0, len = this.items.length; i < len; i++){
10623             this.items[i].purgeListeners();
10624         }
10625         if(removeEl === true){
10626             this.el.update("");
10627             this.el.remove();
10628         }
10629     }
10630 });
10631
10632 /**
10633  * @class Roo.TabPanelItem
10634  * @extends Roo.util.Observable
10635  * Represents an individual item (tab plus body) in a TabPanel.
10636  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10637  * @param {String} id The id of this TabPanelItem
10638  * @param {String} text The text for the tab of this TabPanelItem
10639  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10640  */
10641 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10642     /**
10643      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10644      * @type Roo.TabPanel
10645      */
10646     this.tabPanel = tabPanel;
10647     /**
10648      * The id for this TabPanelItem
10649      * @type String
10650      */
10651     this.id = id;
10652     /** @private */
10653     this.disabled = false;
10654     /** @private */
10655     this.text = text;
10656     /** @private */
10657     this.loaded = false;
10658     this.closable = closable;
10659
10660     /**
10661      * The body element for this TabPanelItem.
10662      * @type Roo.Element
10663      */
10664     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10665     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10666     this.bodyEl.setStyle("display", "block");
10667     this.bodyEl.setStyle("zoom", "1");
10668     this.hideAction();
10669
10670     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10671     /** @private */
10672     this.el = Roo.get(els.el, true);
10673     this.inner = Roo.get(els.inner, true);
10674     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10675     this.pnode = Roo.get(els.el.parentNode, true);
10676     this.el.on("mousedown", this.onTabMouseDown, this);
10677     this.el.on("click", this.onTabClick, this);
10678     /** @private */
10679     if(closable){
10680         var c = Roo.get(els.close, true);
10681         c.dom.title = this.closeText;
10682         c.addClassOnOver("close-over");
10683         c.on("click", this.closeClick, this);
10684      }
10685
10686     this.addEvents({
10687          /**
10688          * @event activate
10689          * Fires when this tab becomes the active tab.
10690          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10691          * @param {Roo.TabPanelItem} this
10692          */
10693         "activate": true,
10694         /**
10695          * @event beforeclose
10696          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10697          * @param {Roo.TabPanelItem} this
10698          * @param {Object} e Set cancel to true on this object to cancel the close.
10699          */
10700         "beforeclose": true,
10701         /**
10702          * @event close
10703          * Fires when this tab is closed.
10704          * @param {Roo.TabPanelItem} this
10705          */
10706          "close": true,
10707         /**
10708          * @event deactivate
10709          * Fires when this tab is no longer the active tab.
10710          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10711          * @param {Roo.TabPanelItem} this
10712          */
10713          "deactivate" : true
10714     });
10715     this.hidden = false;
10716
10717     Roo.TabPanelItem.superclass.constructor.call(this);
10718 };
10719
10720 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10721     purgeListeners : function(){
10722        Roo.util.Observable.prototype.purgeListeners.call(this);
10723        this.el.removeAllListeners();
10724     },
10725     /**
10726      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10727      */
10728     show : function(){
10729         this.pnode.addClass("on");
10730         this.showAction();
10731         if(Roo.isOpera){
10732             this.tabPanel.stripWrap.repaint();
10733         }
10734         this.fireEvent("activate", this.tabPanel, this);
10735     },
10736
10737     /**
10738      * Returns true if this tab is the active tab.
10739      * @return {Boolean}
10740      */
10741     isActive : function(){
10742         return this.tabPanel.getActiveTab() == this;
10743     },
10744
10745     /**
10746      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10747      */
10748     hide : function(){
10749         this.pnode.removeClass("on");
10750         this.hideAction();
10751         this.fireEvent("deactivate", this.tabPanel, this);
10752     },
10753
10754     hideAction : function(){
10755         this.bodyEl.hide();
10756         this.bodyEl.setStyle("position", "absolute");
10757         this.bodyEl.setLeft("-20000px");
10758         this.bodyEl.setTop("-20000px");
10759     },
10760
10761     showAction : function(){
10762         this.bodyEl.setStyle("position", "relative");
10763         this.bodyEl.setTop("");
10764         this.bodyEl.setLeft("");
10765         this.bodyEl.show();
10766     },
10767
10768     /**
10769      * Set the tooltip for the tab.
10770      * @param {String} tooltip The tab's tooltip
10771      */
10772     setTooltip : function(text){
10773         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10774             this.textEl.dom.qtip = text;
10775             this.textEl.dom.removeAttribute('title');
10776         }else{
10777             this.textEl.dom.title = text;
10778         }
10779     },
10780
10781     onTabClick : function(e){
10782         e.preventDefault();
10783         this.tabPanel.activate(this.id);
10784     },
10785
10786     onTabMouseDown : function(e){
10787         e.preventDefault();
10788         this.tabPanel.activate(this.id);
10789     },
10790
10791     getWidth : function(){
10792         return this.inner.getWidth();
10793     },
10794
10795     setWidth : function(width){
10796         var iwidth = width - this.pnode.getPadding("lr");
10797         this.inner.setWidth(iwidth);
10798         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10799         this.pnode.setWidth(width);
10800     },
10801
10802     /**
10803      * Show or hide the tab
10804      * @param {Boolean} hidden True to hide or false to show.
10805      */
10806     setHidden : function(hidden){
10807         this.hidden = hidden;
10808         this.pnode.setStyle("display", hidden ? "none" : "");
10809     },
10810
10811     /**
10812      * Returns true if this tab is "hidden"
10813      * @return {Boolean}
10814      */
10815     isHidden : function(){
10816         return this.hidden;
10817     },
10818
10819     /**
10820      * Returns the text for this tab
10821      * @return {String}
10822      */
10823     getText : function(){
10824         return this.text;
10825     },
10826
10827     autoSize : function(){
10828         //this.el.beginMeasure();
10829         this.textEl.setWidth(1);
10830         /*
10831          *  #2804 [new] Tabs in Roojs
10832          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10833          */
10834         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10835         //this.el.endMeasure();
10836     },
10837
10838     /**
10839      * Sets the text for the tab (Note: this also sets the tooltip text)
10840      * @param {String} text The tab's text and tooltip
10841      */
10842     setText : function(text){
10843         this.text = text;
10844         this.textEl.update(text);
10845         this.setTooltip(text);
10846         if(!this.tabPanel.resizeTabs){
10847             this.autoSize();
10848         }
10849     },
10850     /**
10851      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10852      */
10853     activate : function(){
10854         this.tabPanel.activate(this.id);
10855     },
10856
10857     /**
10858      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10859      */
10860     disable : function(){
10861         if(this.tabPanel.active != this){
10862             this.disabled = true;
10863             this.pnode.addClass("disabled");
10864         }
10865     },
10866
10867     /**
10868      * Enables this TabPanelItem if it was previously disabled.
10869      */
10870     enable : function(){
10871         this.disabled = false;
10872         this.pnode.removeClass("disabled");
10873     },
10874
10875     /**
10876      * Sets the content for this TabPanelItem.
10877      * @param {String} content The content
10878      * @param {Boolean} loadScripts true to look for and load scripts
10879      */
10880     setContent : function(content, loadScripts){
10881         this.bodyEl.update(content, loadScripts);
10882     },
10883
10884     /**
10885      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10886      * @return {Roo.UpdateManager} The UpdateManager
10887      */
10888     getUpdateManager : function(){
10889         return this.bodyEl.getUpdateManager();
10890     },
10891
10892     /**
10893      * Set a URL to be used to load the content for this TabPanelItem.
10894      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10895      * @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)
10896      * @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)
10897      * @return {Roo.UpdateManager} The UpdateManager
10898      */
10899     setUrl : function(url, params, loadOnce){
10900         if(this.refreshDelegate){
10901             this.un('activate', this.refreshDelegate);
10902         }
10903         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10904         this.on("activate", this.refreshDelegate);
10905         return this.bodyEl.getUpdateManager();
10906     },
10907
10908     /** @private */
10909     _handleRefresh : function(url, params, loadOnce){
10910         if(!loadOnce || !this.loaded){
10911             var updater = this.bodyEl.getUpdateManager();
10912             updater.update(url, params, this._setLoaded.createDelegate(this));
10913         }
10914     },
10915
10916     /**
10917      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10918      *   Will fail silently if the setUrl method has not been called.
10919      *   This does not activate the panel, just updates its content.
10920      */
10921     refresh : function(){
10922         if(this.refreshDelegate){
10923            this.loaded = false;
10924            this.refreshDelegate();
10925         }
10926     },
10927
10928     /** @private */
10929     _setLoaded : function(){
10930         this.loaded = true;
10931     },
10932
10933     /** @private */
10934     closeClick : function(e){
10935         var o = {};
10936         e.stopEvent();
10937         this.fireEvent("beforeclose", this, o);
10938         if(o.cancel !== true){
10939             this.tabPanel.removeTab(this.id);
10940         }
10941     },
10942     /**
10943      * The text displayed in the tooltip for the close icon.
10944      * @type String
10945      */
10946     closeText : "Close this tab"
10947 });
10948
10949 /** @private */
10950 Roo.TabPanel.prototype.createStrip = function(container){
10951     var strip = document.createElement("div");
10952     strip.className = "x-tabs-wrap";
10953     container.appendChild(strip);
10954     return strip;
10955 };
10956 /** @private */
10957 Roo.TabPanel.prototype.createStripList = function(strip){
10958     // div wrapper for retard IE
10959     // returns the "tr" element.
10960     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10961         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10962         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10963     return strip.firstChild.firstChild.firstChild.firstChild;
10964 };
10965 /** @private */
10966 Roo.TabPanel.prototype.createBody = function(container){
10967     var body = document.createElement("div");
10968     Roo.id(body, "tab-body");
10969     Roo.fly(body).addClass("x-tabs-body");
10970     container.appendChild(body);
10971     return body;
10972 };
10973 /** @private */
10974 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10975     var body = Roo.getDom(id);
10976     if(!body){
10977         body = document.createElement("div");
10978         body.id = id;
10979     }
10980     Roo.fly(body).addClass("x-tabs-item-body");
10981     bodyEl.insertBefore(body, bodyEl.firstChild);
10982     return body;
10983 };
10984 /** @private */
10985 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10986     var td = document.createElement("td");
10987     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10988     //stripEl.appendChild(td);
10989     if(closable){
10990         td.className = "x-tabs-closable";
10991         if(!this.closeTpl){
10992             this.closeTpl = new Roo.Template(
10993                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10994                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10995                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10996             );
10997         }
10998         var el = this.closeTpl.overwrite(td, {"text": text});
10999         var close = el.getElementsByTagName("div")[0];
11000         var inner = el.getElementsByTagName("em")[0];
11001         return {"el": el, "close": close, "inner": inner};
11002     } else {
11003         if(!this.tabTpl){
11004             this.tabTpl = new Roo.Template(
11005                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11006                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11007             );
11008         }
11009         var el = this.tabTpl.overwrite(td, {"text": text});
11010         var inner = el.getElementsByTagName("em")[0];
11011         return {"el": el, "inner": inner};
11012     }
11013 };/*
11014  * Based on:
11015  * Ext JS Library 1.1.1
11016  * Copyright(c) 2006-2007, Ext JS, LLC.
11017  *
11018  * Originally Released Under LGPL - original licence link has changed is not relivant.
11019  *
11020  * Fork - LGPL
11021  * <script type="text/javascript">
11022  */
11023
11024 /**
11025  * @class Roo.Button
11026  * @extends Roo.util.Observable
11027  * Simple Button class
11028  * @cfg {String} text The button text
11029  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11030  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11031  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11032  * @cfg {Object} scope The scope of the handler
11033  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11034  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11035  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11036  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11037  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11038  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11039    applies if enableToggle = true)
11040  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11041  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11042   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11043  * @constructor
11044  * Create a new button
11045  * @param {Object} config The config object
11046  */
11047 Roo.Button = function(renderTo, config)
11048 {
11049     if (!config) {
11050         config = renderTo;
11051         renderTo = config.renderTo || false;
11052     }
11053     
11054     Roo.apply(this, config);
11055     this.addEvents({
11056         /**
11057              * @event click
11058              * Fires when this button is clicked
11059              * @param {Button} this
11060              * @param {EventObject} e The click event
11061              */
11062             "click" : true,
11063         /**
11064              * @event toggle
11065              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11066              * @param {Button} this
11067              * @param {Boolean} pressed
11068              */
11069             "toggle" : true,
11070         /**
11071              * @event mouseover
11072              * Fires when the mouse hovers over the button
11073              * @param {Button} this
11074              * @param {Event} e The event object
11075              */
11076         'mouseover' : true,
11077         /**
11078              * @event mouseout
11079              * Fires when the mouse exits the button
11080              * @param {Button} this
11081              * @param {Event} e The event object
11082              */
11083         'mouseout': true,
11084          /**
11085              * @event render
11086              * Fires when the button is rendered
11087              * @param {Button} this
11088              */
11089         'render': true
11090     });
11091     if(this.menu){
11092         this.menu = Roo.menu.MenuMgr.get(this.menu);
11093     }
11094     // register listeners first!!  - so render can be captured..
11095     Roo.util.Observable.call(this);
11096     if(renderTo){
11097         this.render(renderTo);
11098     }
11099     
11100   
11101 };
11102
11103 Roo.extend(Roo.Button, Roo.util.Observable, {
11104     /**
11105      * 
11106      */
11107     
11108     /**
11109      * Read-only. True if this button is hidden
11110      * @type Boolean
11111      */
11112     hidden : false,
11113     /**
11114      * Read-only. True if this button is disabled
11115      * @type Boolean
11116      */
11117     disabled : false,
11118     /**
11119      * Read-only. True if this button is pressed (only if enableToggle = true)
11120      * @type Boolean
11121      */
11122     pressed : false,
11123
11124     /**
11125      * @cfg {Number} tabIndex 
11126      * The DOM tabIndex for this button (defaults to undefined)
11127      */
11128     tabIndex : undefined,
11129
11130     /**
11131      * @cfg {Boolean} enableToggle
11132      * True to enable pressed/not pressed toggling (defaults to false)
11133      */
11134     enableToggle: false,
11135     /**
11136      * @cfg {Mixed} menu
11137      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11138      */
11139     menu : undefined,
11140     /**
11141      * @cfg {String} menuAlign
11142      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11143      */
11144     menuAlign : "tl-bl?",
11145
11146     /**
11147      * @cfg {String} iconCls
11148      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11149      */
11150     iconCls : undefined,
11151     /**
11152      * @cfg {String} type
11153      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11154      */
11155     type : 'button',
11156
11157     // private
11158     menuClassTarget: 'tr',
11159
11160     /**
11161      * @cfg {String} clickEvent
11162      * The type of event to map to the button's event handler (defaults to 'click')
11163      */
11164     clickEvent : 'click',
11165
11166     /**
11167      * @cfg {Boolean} handleMouseEvents
11168      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11169      */
11170     handleMouseEvents : true,
11171
11172     /**
11173      * @cfg {String} tooltipType
11174      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11175      */
11176     tooltipType : 'qtip',
11177
11178     /**
11179      * @cfg {String} cls
11180      * A CSS class to apply to the button's main element.
11181      */
11182     
11183     /**
11184      * @cfg {Roo.Template} template (Optional)
11185      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11186      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11187      * require code modifications if required elements (e.g. a button) aren't present.
11188      */
11189
11190     // private
11191     render : function(renderTo){
11192         var btn;
11193         if(this.hideParent){
11194             this.parentEl = Roo.get(renderTo);
11195         }
11196         if(!this.dhconfig){
11197             if(!this.template){
11198                 if(!Roo.Button.buttonTemplate){
11199                     // hideous table template
11200                     Roo.Button.buttonTemplate = new Roo.Template(
11201                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11202                         '<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>',
11203                         "</tr></tbody></table>");
11204                 }
11205                 this.template = Roo.Button.buttonTemplate;
11206             }
11207             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11208             var btnEl = btn.child("button:first");
11209             btnEl.on('focus', this.onFocus, this);
11210             btnEl.on('blur', this.onBlur, this);
11211             if(this.cls){
11212                 btn.addClass(this.cls);
11213             }
11214             if(this.icon){
11215                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11216             }
11217             if(this.iconCls){
11218                 btnEl.addClass(this.iconCls);
11219                 if(!this.cls){
11220                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11221                 }
11222             }
11223             if(this.tabIndex !== undefined){
11224                 btnEl.dom.tabIndex = this.tabIndex;
11225             }
11226             if(this.tooltip){
11227                 if(typeof this.tooltip == 'object'){
11228                     Roo.QuickTips.tips(Roo.apply({
11229                           target: btnEl.id
11230                     }, this.tooltip));
11231                 } else {
11232                     btnEl.dom[this.tooltipType] = this.tooltip;
11233                 }
11234             }
11235         }else{
11236             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11237         }
11238         this.el = btn;
11239         if(this.id){
11240             this.el.dom.id = this.el.id = this.id;
11241         }
11242         if(this.menu){
11243             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11244             this.menu.on("show", this.onMenuShow, this);
11245             this.menu.on("hide", this.onMenuHide, this);
11246         }
11247         btn.addClass("x-btn");
11248         if(Roo.isIE && !Roo.isIE7){
11249             this.autoWidth.defer(1, this);
11250         }else{
11251             this.autoWidth();
11252         }
11253         if(this.handleMouseEvents){
11254             btn.on("mouseover", this.onMouseOver, this);
11255             btn.on("mouseout", this.onMouseOut, this);
11256             btn.on("mousedown", this.onMouseDown, this);
11257         }
11258         btn.on(this.clickEvent, this.onClick, this);
11259         //btn.on("mouseup", this.onMouseUp, this);
11260         if(this.hidden){
11261             this.hide();
11262         }
11263         if(this.disabled){
11264             this.disable();
11265         }
11266         Roo.ButtonToggleMgr.register(this);
11267         if(this.pressed){
11268             this.el.addClass("x-btn-pressed");
11269         }
11270         if(this.repeat){
11271             var repeater = new Roo.util.ClickRepeater(btn,
11272                 typeof this.repeat == "object" ? this.repeat : {}
11273             );
11274             repeater.on("click", this.onClick,  this);
11275         }
11276         
11277         this.fireEvent('render', this);
11278         
11279     },
11280     /**
11281      * Returns the button's underlying element
11282      * @return {Roo.Element} The element
11283      */
11284     getEl : function(){
11285         return this.el;  
11286     },
11287     
11288     /**
11289      * Destroys this Button and removes any listeners.
11290      */
11291     destroy : function(){
11292         Roo.ButtonToggleMgr.unregister(this);
11293         this.el.removeAllListeners();
11294         this.purgeListeners();
11295         this.el.remove();
11296     },
11297
11298     // private
11299     autoWidth : function(){
11300         if(this.el){
11301             this.el.setWidth("auto");
11302             if(Roo.isIE7 && Roo.isStrict){
11303                 var ib = this.el.child('button');
11304                 if(ib && ib.getWidth() > 20){
11305                     ib.clip();
11306                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11307                 }
11308             }
11309             if(this.minWidth){
11310                 if(this.hidden){
11311                     this.el.beginMeasure();
11312                 }
11313                 if(this.el.getWidth() < this.minWidth){
11314                     this.el.setWidth(this.minWidth);
11315                 }
11316                 if(this.hidden){
11317                     this.el.endMeasure();
11318                 }
11319             }
11320         }
11321     },
11322
11323     /**
11324      * Assigns this button's click handler
11325      * @param {Function} handler The function to call when the button is clicked
11326      * @param {Object} scope (optional) Scope for the function passed in
11327      */
11328     setHandler : function(handler, scope){
11329         this.handler = handler;
11330         this.scope = scope;  
11331     },
11332     
11333     /**
11334      * Sets this button's text
11335      * @param {String} text The button text
11336      */
11337     setText : function(text){
11338         this.text = text;
11339         if(this.el){
11340             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11341         }
11342         this.autoWidth();
11343     },
11344     
11345     /**
11346      * Gets the text for this button
11347      * @return {String} The button text
11348      */
11349     getText : function(){
11350         return this.text;  
11351     },
11352     
11353     /**
11354      * Show this button
11355      */
11356     show: function(){
11357         this.hidden = false;
11358         if(this.el){
11359             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11360         }
11361     },
11362     
11363     /**
11364      * Hide this button
11365      */
11366     hide: function(){
11367         this.hidden = true;
11368         if(this.el){
11369             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11370         }
11371     },
11372     
11373     /**
11374      * Convenience function for boolean show/hide
11375      * @param {Boolean} visible True to show, false to hide
11376      */
11377     setVisible: function(visible){
11378         if(visible) {
11379             this.show();
11380         }else{
11381             this.hide();
11382         }
11383     },
11384     
11385     /**
11386      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11387      * @param {Boolean} state (optional) Force a particular state
11388      */
11389     toggle : function(state){
11390         state = state === undefined ? !this.pressed : state;
11391         if(state != this.pressed){
11392             if(state){
11393                 this.el.addClass("x-btn-pressed");
11394                 this.pressed = true;
11395                 this.fireEvent("toggle", this, true);
11396             }else{
11397                 this.el.removeClass("x-btn-pressed");
11398                 this.pressed = false;
11399                 this.fireEvent("toggle", this, false);
11400             }
11401             if(this.toggleHandler){
11402                 this.toggleHandler.call(this.scope || this, this, state);
11403             }
11404         }
11405     },
11406     
11407     /**
11408      * Focus the button
11409      */
11410     focus : function(){
11411         this.el.child('button:first').focus();
11412     },
11413     
11414     /**
11415      * Disable this button
11416      */
11417     disable : function(){
11418         if(this.el){
11419             this.el.addClass("x-btn-disabled");
11420         }
11421         this.disabled = true;
11422     },
11423     
11424     /**
11425      * Enable this button
11426      */
11427     enable : function(){
11428         if(this.el){
11429             this.el.removeClass("x-btn-disabled");
11430         }
11431         this.disabled = false;
11432     },
11433
11434     /**
11435      * Convenience function for boolean enable/disable
11436      * @param {Boolean} enabled True to enable, false to disable
11437      */
11438     setDisabled : function(v){
11439         this[v !== true ? "enable" : "disable"]();
11440     },
11441
11442     // private
11443     onClick : function(e)
11444     {
11445         if(e){
11446             e.preventDefault();
11447         }
11448         if(e.button != 0){
11449             return;
11450         }
11451         if(!this.disabled){
11452             if(this.enableToggle){
11453                 this.toggle();
11454             }
11455             if(this.menu && !this.menu.isVisible()){
11456                 this.menu.show(this.el, this.menuAlign);
11457             }
11458             this.fireEvent("click", this, e);
11459             if(this.handler){
11460                 this.el.removeClass("x-btn-over");
11461                 this.handler.call(this.scope || this, this, e);
11462             }
11463         }
11464     },
11465     // private
11466     onMouseOver : function(e){
11467         if(!this.disabled){
11468             this.el.addClass("x-btn-over");
11469             this.fireEvent('mouseover', this, e);
11470         }
11471     },
11472     // private
11473     onMouseOut : function(e){
11474         if(!e.within(this.el,  true)){
11475             this.el.removeClass("x-btn-over");
11476             this.fireEvent('mouseout', this, e);
11477         }
11478     },
11479     // private
11480     onFocus : function(e){
11481         if(!this.disabled){
11482             this.el.addClass("x-btn-focus");
11483         }
11484     },
11485     // private
11486     onBlur : function(e){
11487         this.el.removeClass("x-btn-focus");
11488     },
11489     // private
11490     onMouseDown : function(e){
11491         if(!this.disabled && e.button == 0){
11492             this.el.addClass("x-btn-click");
11493             Roo.get(document).on('mouseup', this.onMouseUp, this);
11494         }
11495     },
11496     // private
11497     onMouseUp : function(e){
11498         if(e.button == 0){
11499             this.el.removeClass("x-btn-click");
11500             Roo.get(document).un('mouseup', this.onMouseUp, this);
11501         }
11502     },
11503     // private
11504     onMenuShow : function(e){
11505         this.el.addClass("x-btn-menu-active");
11506     },
11507     // private
11508     onMenuHide : function(e){
11509         this.el.removeClass("x-btn-menu-active");
11510     }   
11511 });
11512
11513 // Private utility class used by Button
11514 Roo.ButtonToggleMgr = function(){
11515    var groups = {};
11516    
11517    function toggleGroup(btn, state){
11518        if(state){
11519            var g = groups[btn.toggleGroup];
11520            for(var i = 0, l = g.length; i < l; i++){
11521                if(g[i] != btn){
11522                    g[i].toggle(false);
11523                }
11524            }
11525        }
11526    }
11527    
11528    return {
11529        register : function(btn){
11530            if(!btn.toggleGroup){
11531                return;
11532            }
11533            var g = groups[btn.toggleGroup];
11534            if(!g){
11535                g = groups[btn.toggleGroup] = [];
11536            }
11537            g.push(btn);
11538            btn.on("toggle", toggleGroup);
11539        },
11540        
11541        unregister : function(btn){
11542            if(!btn.toggleGroup){
11543                return;
11544            }
11545            var g = groups[btn.toggleGroup];
11546            if(g){
11547                g.remove(btn);
11548                btn.un("toggle", toggleGroup);
11549            }
11550        }
11551    };
11552 }();/*
11553  * Based on:
11554  * Ext JS Library 1.1.1
11555  * Copyright(c) 2006-2007, Ext JS, LLC.
11556  *
11557  * Originally Released Under LGPL - original licence link has changed is not relivant.
11558  *
11559  * Fork - LGPL
11560  * <script type="text/javascript">
11561  */
11562  
11563 /**
11564  * @class Roo.SplitButton
11565  * @extends Roo.Button
11566  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11567  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11568  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11569  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11570  * @cfg {String} arrowTooltip The title attribute of the arrow
11571  * @constructor
11572  * Create a new menu button
11573  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11574  * @param {Object} config The config object
11575  */
11576 Roo.SplitButton = function(renderTo, config){
11577     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11578     /**
11579      * @event arrowclick
11580      * Fires when this button's arrow is clicked
11581      * @param {SplitButton} this
11582      * @param {EventObject} e The click event
11583      */
11584     this.addEvents({"arrowclick":true});
11585 };
11586
11587 Roo.extend(Roo.SplitButton, Roo.Button, {
11588     render : function(renderTo){
11589         // this is one sweet looking template!
11590         var tpl = new Roo.Template(
11591             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11592             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11593             '<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>',
11594             "</tbody></table></td><td>",
11595             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11596             '<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>',
11597             "</tbody></table></td></tr></table>"
11598         );
11599         var btn = tpl.append(renderTo, [this.text, this.type], true);
11600         var btnEl = btn.child("button");
11601         if(this.cls){
11602             btn.addClass(this.cls);
11603         }
11604         if(this.icon){
11605             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11606         }
11607         if(this.iconCls){
11608             btnEl.addClass(this.iconCls);
11609             if(!this.cls){
11610                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11611             }
11612         }
11613         this.el = btn;
11614         if(this.handleMouseEvents){
11615             btn.on("mouseover", this.onMouseOver, this);
11616             btn.on("mouseout", this.onMouseOut, this);
11617             btn.on("mousedown", this.onMouseDown, this);
11618             btn.on("mouseup", this.onMouseUp, this);
11619         }
11620         btn.on(this.clickEvent, this.onClick, this);
11621         if(this.tooltip){
11622             if(typeof this.tooltip == 'object'){
11623                 Roo.QuickTips.tips(Roo.apply({
11624                       target: btnEl.id
11625                 }, this.tooltip));
11626             } else {
11627                 btnEl.dom[this.tooltipType] = this.tooltip;
11628             }
11629         }
11630         if(this.arrowTooltip){
11631             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11632         }
11633         if(this.hidden){
11634             this.hide();
11635         }
11636         if(this.disabled){
11637             this.disable();
11638         }
11639         if(this.pressed){
11640             this.el.addClass("x-btn-pressed");
11641         }
11642         if(Roo.isIE && !Roo.isIE7){
11643             this.autoWidth.defer(1, this);
11644         }else{
11645             this.autoWidth();
11646         }
11647         if(this.menu){
11648             this.menu.on("show", this.onMenuShow, this);
11649             this.menu.on("hide", this.onMenuHide, this);
11650         }
11651         this.fireEvent('render', this);
11652     },
11653
11654     // private
11655     autoWidth : function(){
11656         if(this.el){
11657             var tbl = this.el.child("table:first");
11658             var tbl2 = this.el.child("table:last");
11659             this.el.setWidth("auto");
11660             tbl.setWidth("auto");
11661             if(Roo.isIE7 && Roo.isStrict){
11662                 var ib = this.el.child('button:first');
11663                 if(ib && ib.getWidth() > 20){
11664                     ib.clip();
11665                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11666                 }
11667             }
11668             if(this.minWidth){
11669                 if(this.hidden){
11670                     this.el.beginMeasure();
11671                 }
11672                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11673                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11674                 }
11675                 if(this.hidden){
11676                     this.el.endMeasure();
11677                 }
11678             }
11679             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11680         } 
11681     },
11682     /**
11683      * Sets this button's click handler
11684      * @param {Function} handler The function to call when the button is clicked
11685      * @param {Object} scope (optional) Scope for the function passed above
11686      */
11687     setHandler : function(handler, scope){
11688         this.handler = handler;
11689         this.scope = scope;  
11690     },
11691     
11692     /**
11693      * Sets this button's arrow click handler
11694      * @param {Function} handler The function to call when the arrow is clicked
11695      * @param {Object} scope (optional) Scope for the function passed above
11696      */
11697     setArrowHandler : function(handler, scope){
11698         this.arrowHandler = handler;
11699         this.scope = scope;  
11700     },
11701     
11702     /**
11703      * Focus the button
11704      */
11705     focus : function(){
11706         if(this.el){
11707             this.el.child("button:first").focus();
11708         }
11709     },
11710
11711     // private
11712     onClick : function(e){
11713         e.preventDefault();
11714         if(!this.disabled){
11715             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11716                 if(this.menu && !this.menu.isVisible()){
11717                     this.menu.show(this.el, this.menuAlign);
11718                 }
11719                 this.fireEvent("arrowclick", this, e);
11720                 if(this.arrowHandler){
11721                     this.arrowHandler.call(this.scope || this, this, e);
11722                 }
11723             }else{
11724                 this.fireEvent("click", this, e);
11725                 if(this.handler){
11726                     this.handler.call(this.scope || this, this, e);
11727                 }
11728             }
11729         }
11730     },
11731     // private
11732     onMouseDown : function(e){
11733         if(!this.disabled){
11734             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11735         }
11736     },
11737     // private
11738     onMouseUp : function(e){
11739         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11740     }   
11741 });
11742
11743
11744 // backwards compat
11745 Roo.MenuButton = Roo.SplitButton;/*
11746  * Based on:
11747  * Ext JS Library 1.1.1
11748  * Copyright(c) 2006-2007, Ext JS, LLC.
11749  *
11750  * Originally Released Under LGPL - original licence link has changed is not relivant.
11751  *
11752  * Fork - LGPL
11753  * <script type="text/javascript">
11754  */
11755
11756 /**
11757  * @class Roo.Toolbar
11758  * Basic Toolbar class.
11759  * @constructor
11760  * Creates a new Toolbar
11761  * @param {Object} container The config object
11762  */ 
11763 Roo.Toolbar = function(container, buttons, config)
11764 {
11765     /// old consturctor format still supported..
11766     if(container instanceof Array){ // omit the container for later rendering
11767         buttons = container;
11768         config = buttons;
11769         container = null;
11770     }
11771     if (typeof(container) == 'object' && container.xtype) {
11772         config = container;
11773         container = config.container;
11774         buttons = config.buttons || []; // not really - use items!!
11775     }
11776     var xitems = [];
11777     if (config && config.items) {
11778         xitems = config.items;
11779         delete config.items;
11780     }
11781     Roo.apply(this, config);
11782     this.buttons = buttons;
11783     
11784     if(container){
11785         this.render(container);
11786     }
11787     this.xitems = xitems;
11788     Roo.each(xitems, function(b) {
11789         this.add(b);
11790     }, this);
11791     
11792 };
11793
11794 Roo.Toolbar.prototype = {
11795     /**
11796      * @cfg {Array} items
11797      * array of button configs or elements to add (will be converted to a MixedCollection)
11798      */
11799     
11800     /**
11801      * @cfg {String/HTMLElement/Element} container
11802      * The id or element that will contain the toolbar
11803      */
11804     // private
11805     render : function(ct){
11806         this.el = Roo.get(ct);
11807         if(this.cls){
11808             this.el.addClass(this.cls);
11809         }
11810         // using a table allows for vertical alignment
11811         // 100% width is needed by Safari...
11812         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11813         this.tr = this.el.child("tr", true);
11814         var autoId = 0;
11815         this.items = new Roo.util.MixedCollection(false, function(o){
11816             return o.id || ("item" + (++autoId));
11817         });
11818         if(this.buttons){
11819             this.add.apply(this, this.buttons);
11820             delete this.buttons;
11821         }
11822     },
11823
11824     /**
11825      * Adds element(s) to the toolbar -- this function takes a variable number of 
11826      * arguments of mixed type and adds them to the toolbar.
11827      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11828      * <ul>
11829      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11830      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11831      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11832      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11833      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11834      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11835      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11836      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11837      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11838      * </ul>
11839      * @param {Mixed} arg2
11840      * @param {Mixed} etc.
11841      */
11842     add : function(){
11843         var a = arguments, l = a.length;
11844         for(var i = 0; i < l; i++){
11845             this._add(a[i]);
11846         }
11847     },
11848     // private..
11849     _add : function(el) {
11850         
11851         if (el.xtype) {
11852             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11853         }
11854         
11855         if (el.applyTo){ // some kind of form field
11856             return this.addField(el);
11857         } 
11858         if (el.render){ // some kind of Toolbar.Item
11859             return this.addItem(el);
11860         }
11861         if (typeof el == "string"){ // string
11862             if(el == "separator" || el == "-"){
11863                 return this.addSeparator();
11864             }
11865             if (el == " "){
11866                 return this.addSpacer();
11867             }
11868             if(el == "->"){
11869                 return this.addFill();
11870             }
11871             return this.addText(el);
11872             
11873         }
11874         if(el.tagName){ // element
11875             return this.addElement(el);
11876         }
11877         if(typeof el == "object"){ // must be button config?
11878             return this.addButton(el);
11879         }
11880         // and now what?!?!
11881         return false;
11882         
11883     },
11884     
11885     /**
11886      * Add an Xtype element
11887      * @param {Object} xtype Xtype Object
11888      * @return {Object} created Object
11889      */
11890     addxtype : function(e){
11891         return this.add(e);  
11892     },
11893     
11894     /**
11895      * Returns the Element for this toolbar.
11896      * @return {Roo.Element}
11897      */
11898     getEl : function(){
11899         return this.el;  
11900     },
11901     
11902     /**
11903      * Adds a separator
11904      * @return {Roo.Toolbar.Item} The separator item
11905      */
11906     addSeparator : function(){
11907         return this.addItem(new Roo.Toolbar.Separator());
11908     },
11909
11910     /**
11911      * Adds a spacer element
11912      * @return {Roo.Toolbar.Spacer} The spacer item
11913      */
11914     addSpacer : function(){
11915         return this.addItem(new Roo.Toolbar.Spacer());
11916     },
11917
11918     /**
11919      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11920      * @return {Roo.Toolbar.Fill} The fill item
11921      */
11922     addFill : function(){
11923         return this.addItem(new Roo.Toolbar.Fill());
11924     },
11925
11926     /**
11927      * Adds any standard HTML element to the toolbar
11928      * @param {String/HTMLElement/Element} el The element or id of the element to add
11929      * @return {Roo.Toolbar.Item} The element's item
11930      */
11931     addElement : function(el){
11932         return this.addItem(new Roo.Toolbar.Item(el));
11933     },
11934     /**
11935      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11936      * @type Roo.util.MixedCollection  
11937      */
11938     items : false,
11939      
11940     /**
11941      * Adds any Toolbar.Item or subclass
11942      * @param {Roo.Toolbar.Item} item
11943      * @return {Roo.Toolbar.Item} The item
11944      */
11945     addItem : function(item){
11946         var td = this.nextBlock();
11947         item.render(td);
11948         this.items.add(item);
11949         return item;
11950     },
11951     
11952     /**
11953      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11954      * @param {Object/Array} config A button config or array of configs
11955      * @return {Roo.Toolbar.Button/Array}
11956      */
11957     addButton : function(config){
11958         if(config instanceof Array){
11959             var buttons = [];
11960             for(var i = 0, len = config.length; i < len; i++) {
11961                 buttons.push(this.addButton(config[i]));
11962             }
11963             return buttons;
11964         }
11965         var b = config;
11966         if(!(config instanceof Roo.Toolbar.Button)){
11967             b = config.split ?
11968                 new Roo.Toolbar.SplitButton(config) :
11969                 new Roo.Toolbar.Button(config);
11970         }
11971         var td = this.nextBlock();
11972         b.render(td);
11973         this.items.add(b);
11974         return b;
11975     },
11976     
11977     /**
11978      * Adds text to the toolbar
11979      * @param {String} text The text to add
11980      * @return {Roo.Toolbar.Item} The element's item
11981      */
11982     addText : function(text){
11983         return this.addItem(new Roo.Toolbar.TextItem(text));
11984     },
11985     
11986     /**
11987      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11988      * @param {Number} index The index where the item is to be inserted
11989      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11990      * @return {Roo.Toolbar.Button/Item}
11991      */
11992     insertButton : function(index, item){
11993         if(item instanceof Array){
11994             var buttons = [];
11995             for(var i = 0, len = item.length; i < len; i++) {
11996                buttons.push(this.insertButton(index + i, item[i]));
11997             }
11998             return buttons;
11999         }
12000         if (!(item instanceof Roo.Toolbar.Button)){
12001            item = new Roo.Toolbar.Button(item);
12002         }
12003         var td = document.createElement("td");
12004         this.tr.insertBefore(td, this.tr.childNodes[index]);
12005         item.render(td);
12006         this.items.insert(index, item);
12007         return item;
12008     },
12009     
12010     /**
12011      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12012      * @param {Object} config
12013      * @return {Roo.Toolbar.Item} The element's item
12014      */
12015     addDom : function(config, returnEl){
12016         var td = this.nextBlock();
12017         Roo.DomHelper.overwrite(td, config);
12018         var ti = new Roo.Toolbar.Item(td.firstChild);
12019         ti.render(td);
12020         this.items.add(ti);
12021         return ti;
12022     },
12023
12024     /**
12025      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12026      * @type Roo.util.MixedCollection  
12027      */
12028     fields : false,
12029     
12030     /**
12031      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12032      * Note: the field should not have been rendered yet. For a field that has already been
12033      * rendered, use {@link #addElement}.
12034      * @param {Roo.form.Field} field
12035      * @return {Roo.ToolbarItem}
12036      */
12037      
12038       
12039     addField : function(field) {
12040         if (!this.fields) {
12041             var autoId = 0;
12042             this.fields = new Roo.util.MixedCollection(false, function(o){
12043                 return o.id || ("item" + (++autoId));
12044             });
12045
12046         }
12047         
12048         var td = this.nextBlock();
12049         field.render(td);
12050         var ti = new Roo.Toolbar.Item(td.firstChild);
12051         ti.render(td);
12052         this.items.add(ti);
12053         this.fields.add(field);
12054         return ti;
12055     },
12056     /**
12057      * Hide the toolbar
12058      * @method hide
12059      */
12060      
12061       
12062     hide : function()
12063     {
12064         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12065         this.el.child('div').hide();
12066     },
12067     /**
12068      * Show the toolbar
12069      * @method show
12070      */
12071     show : function()
12072     {
12073         this.el.child('div').show();
12074     },
12075       
12076     // private
12077     nextBlock : function(){
12078         var td = document.createElement("td");
12079         this.tr.appendChild(td);
12080         return td;
12081     },
12082
12083     // private
12084     destroy : function(){
12085         if(this.items){ // rendered?
12086             Roo.destroy.apply(Roo, this.items.items);
12087         }
12088         if(this.fields){ // rendered?
12089             Roo.destroy.apply(Roo, this.fields.items);
12090         }
12091         Roo.Element.uncache(this.el, this.tr);
12092     }
12093 };
12094
12095 /**
12096  * @class Roo.Toolbar.Item
12097  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12098  * @constructor
12099  * Creates a new Item
12100  * @param {HTMLElement} el 
12101  */
12102 Roo.Toolbar.Item = function(el){
12103     var cfg = {};
12104     if (typeof (el.xtype) != 'undefined') {
12105         cfg = el;
12106         el = cfg.el;
12107     }
12108     
12109     this.el = Roo.getDom(el);
12110     this.id = Roo.id(this.el);
12111     this.hidden = false;
12112     
12113     this.addEvents({
12114          /**
12115              * @event render
12116              * Fires when the button is rendered
12117              * @param {Button} this
12118              */
12119         'render': true
12120     });
12121     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12122 };
12123 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12124 //Roo.Toolbar.Item.prototype = {
12125     
12126     /**
12127      * Get this item's HTML Element
12128      * @return {HTMLElement}
12129      */
12130     getEl : function(){
12131        return this.el;  
12132     },
12133
12134     // private
12135     render : function(td){
12136         
12137          this.td = td;
12138         td.appendChild(this.el);
12139         
12140         this.fireEvent('render', this);
12141     },
12142     
12143     /**
12144      * Removes and destroys this item.
12145      */
12146     destroy : function(){
12147         this.td.parentNode.removeChild(this.td);
12148     },
12149     
12150     /**
12151      * Shows this item.
12152      */
12153     show: function(){
12154         this.hidden = false;
12155         this.td.style.display = "";
12156     },
12157     
12158     /**
12159      * Hides this item.
12160      */
12161     hide: function(){
12162         this.hidden = true;
12163         this.td.style.display = "none";
12164     },
12165     
12166     /**
12167      * Convenience function for boolean show/hide.
12168      * @param {Boolean} visible true to show/false to hide
12169      */
12170     setVisible: function(visible){
12171         if(visible) {
12172             this.show();
12173         }else{
12174             this.hide();
12175         }
12176     },
12177     
12178     /**
12179      * Try to focus this item.
12180      */
12181     focus : function(){
12182         Roo.fly(this.el).focus();
12183     },
12184     
12185     /**
12186      * Disables this item.
12187      */
12188     disable : function(){
12189         Roo.fly(this.td).addClass("x-item-disabled");
12190         this.disabled = true;
12191         this.el.disabled = true;
12192     },
12193     
12194     /**
12195      * Enables this item.
12196      */
12197     enable : function(){
12198         Roo.fly(this.td).removeClass("x-item-disabled");
12199         this.disabled = false;
12200         this.el.disabled = false;
12201     }
12202 });
12203
12204
12205 /**
12206  * @class Roo.Toolbar.Separator
12207  * @extends Roo.Toolbar.Item
12208  * A simple toolbar separator class
12209  * @constructor
12210  * Creates a new Separator
12211  */
12212 Roo.Toolbar.Separator = function(cfg){
12213     
12214     var s = document.createElement("span");
12215     s.className = "ytb-sep";
12216     if (cfg) {
12217         cfg.el = s;
12218     }
12219     
12220     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12221 };
12222 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12223     enable:Roo.emptyFn,
12224     disable:Roo.emptyFn,
12225     focus:Roo.emptyFn
12226 });
12227
12228 /**
12229  * @class Roo.Toolbar.Spacer
12230  * @extends Roo.Toolbar.Item
12231  * A simple element that adds extra horizontal space to a toolbar.
12232  * @constructor
12233  * Creates a new Spacer
12234  */
12235 Roo.Toolbar.Spacer = function(cfg){
12236     var s = document.createElement("div");
12237     s.className = "ytb-spacer";
12238     if (cfg) {
12239         cfg.el = s;
12240     }
12241     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12242 };
12243 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12244     enable:Roo.emptyFn,
12245     disable:Roo.emptyFn,
12246     focus:Roo.emptyFn
12247 });
12248
12249 /**
12250  * @class Roo.Toolbar.Fill
12251  * @extends Roo.Toolbar.Spacer
12252  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12253  * @constructor
12254  * Creates a new Spacer
12255  */
12256 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12257     // private
12258     render : function(td){
12259         td.style.width = '100%';
12260         Roo.Toolbar.Fill.superclass.render.call(this, td);
12261     }
12262 });
12263
12264 /**
12265  * @class Roo.Toolbar.TextItem
12266  * @extends Roo.Toolbar.Item
12267  * A simple class that renders text directly into a toolbar.
12268  * @constructor
12269  * Creates a new TextItem
12270  * @param {String} text
12271  */
12272 Roo.Toolbar.TextItem = function(cfg){
12273     var  text = cfg || "";
12274     if (typeof(cfg) == 'object') {
12275         text = cfg.text || "";
12276     }  else {
12277         cfg = null;
12278     }
12279     var s = document.createElement("span");
12280     s.className = "ytb-text";
12281     s.innerHTML = text;
12282     if (cfg) {
12283         cfg.el  = s;
12284     }
12285     
12286     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12287 };
12288 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12289     
12290      
12291     enable:Roo.emptyFn,
12292     disable:Roo.emptyFn,
12293     focus:Roo.emptyFn
12294 });
12295
12296 /**
12297  * @class Roo.Toolbar.Button
12298  * @extends Roo.Button
12299  * A button that renders into a toolbar.
12300  * @constructor
12301  * Creates a new Button
12302  * @param {Object} config A standard {@link Roo.Button} config object
12303  */
12304 Roo.Toolbar.Button = function(config){
12305     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12306 };
12307 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12308     render : function(td){
12309         this.td = td;
12310         Roo.Toolbar.Button.superclass.render.call(this, td);
12311     },
12312     
12313     /**
12314      * Removes and destroys this button
12315      */
12316     destroy : function(){
12317         Roo.Toolbar.Button.superclass.destroy.call(this);
12318         this.td.parentNode.removeChild(this.td);
12319     },
12320     
12321     /**
12322      * Shows this button
12323      */
12324     show: function(){
12325         this.hidden = false;
12326         this.td.style.display = "";
12327     },
12328     
12329     /**
12330      * Hides this button
12331      */
12332     hide: function(){
12333         this.hidden = true;
12334         this.td.style.display = "none";
12335     },
12336
12337     /**
12338      * Disables this item
12339      */
12340     disable : function(){
12341         Roo.fly(this.td).addClass("x-item-disabled");
12342         this.disabled = true;
12343     },
12344
12345     /**
12346      * Enables this item
12347      */
12348     enable : function(){
12349         Roo.fly(this.td).removeClass("x-item-disabled");
12350         this.disabled = false;
12351     }
12352 });
12353 // backwards compat
12354 Roo.ToolbarButton = Roo.Toolbar.Button;
12355
12356 /**
12357  * @class Roo.Toolbar.SplitButton
12358  * @extends Roo.SplitButton
12359  * A menu button that renders into a toolbar.
12360  * @constructor
12361  * Creates a new SplitButton
12362  * @param {Object} config A standard {@link Roo.SplitButton} config object
12363  */
12364 Roo.Toolbar.SplitButton = function(config){
12365     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12366 };
12367 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12368     render : function(td){
12369         this.td = td;
12370         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12371     },
12372     
12373     /**
12374      * Removes and destroys this button
12375      */
12376     destroy : function(){
12377         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12378         this.td.parentNode.removeChild(this.td);
12379     },
12380     
12381     /**
12382      * Shows this button
12383      */
12384     show: function(){
12385         this.hidden = false;
12386         this.td.style.display = "";
12387     },
12388     
12389     /**
12390      * Hides this button
12391      */
12392     hide: function(){
12393         this.hidden = true;
12394         this.td.style.display = "none";
12395     }
12396 });
12397
12398 // backwards compat
12399 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12400  * Based on:
12401  * Ext JS Library 1.1.1
12402  * Copyright(c) 2006-2007, Ext JS, LLC.
12403  *
12404  * Originally Released Under LGPL - original licence link has changed is not relivant.
12405  *
12406  * Fork - LGPL
12407  * <script type="text/javascript">
12408  */
12409  
12410 /**
12411  * @class Roo.PagingToolbar
12412  * @extends Roo.Toolbar
12413  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12414  * @constructor
12415  * Create a new PagingToolbar
12416  * @param {Object} config The config object
12417  */
12418 Roo.PagingToolbar = function(el, ds, config)
12419 {
12420     // old args format still supported... - xtype is prefered..
12421     if (typeof(el) == 'object' && el.xtype) {
12422         // created from xtype...
12423         config = el;
12424         ds = el.dataSource;
12425         el = config.container;
12426     }
12427     var items = [];
12428     if (config.items) {
12429         items = config.items;
12430         config.items = [];
12431     }
12432     
12433     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12434     this.ds = ds;
12435     this.cursor = 0;
12436     this.renderButtons(this.el);
12437     this.bind(ds);
12438     
12439     // supprot items array.
12440    
12441     Roo.each(items, function(e) {
12442         this.add(Roo.factory(e));
12443     },this);
12444     
12445 };
12446
12447 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12448     /**
12449      * @cfg {Roo.data.Store} dataSource
12450      * The underlying data store providing the paged data
12451      */
12452     /**
12453      * @cfg {String/HTMLElement/Element} container
12454      * container The id or element that will contain the toolbar
12455      */
12456     /**
12457      * @cfg {Boolean} displayInfo
12458      * True to display the displayMsg (defaults to false)
12459      */
12460     /**
12461      * @cfg {Number} pageSize
12462      * The number of records to display per page (defaults to 20)
12463      */
12464     pageSize: 20,
12465     /**
12466      * @cfg {String} displayMsg
12467      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12468      */
12469     displayMsg : 'Displaying {0} - {1} of {2}',
12470     /**
12471      * @cfg {String} emptyMsg
12472      * The message to display when no records are found (defaults to "No data to display")
12473      */
12474     emptyMsg : 'No data to display',
12475     /**
12476      * Customizable piece of the default paging text (defaults to "Page")
12477      * @type String
12478      */
12479     beforePageText : "Page",
12480     /**
12481      * Customizable piece of the default paging text (defaults to "of %0")
12482      * @type String
12483      */
12484     afterPageText : "of {0}",
12485     /**
12486      * Customizable piece of the default paging text (defaults to "First Page")
12487      * @type String
12488      */
12489     firstText : "First Page",
12490     /**
12491      * Customizable piece of the default paging text (defaults to "Previous Page")
12492      * @type String
12493      */
12494     prevText : "Previous Page",
12495     /**
12496      * Customizable piece of the default paging text (defaults to "Next Page")
12497      * @type String
12498      */
12499     nextText : "Next Page",
12500     /**
12501      * Customizable piece of the default paging text (defaults to "Last Page")
12502      * @type String
12503      */
12504     lastText : "Last Page",
12505     /**
12506      * Customizable piece of the default paging text (defaults to "Refresh")
12507      * @type String
12508      */
12509     refreshText : "Refresh",
12510
12511     // private
12512     renderButtons : function(el){
12513         Roo.PagingToolbar.superclass.render.call(this, el);
12514         this.first = this.addButton({
12515             tooltip: this.firstText,
12516             cls: "x-btn-icon x-grid-page-first",
12517             disabled: true,
12518             handler: this.onClick.createDelegate(this, ["first"])
12519         });
12520         this.prev = this.addButton({
12521             tooltip: this.prevText,
12522             cls: "x-btn-icon x-grid-page-prev",
12523             disabled: true,
12524             handler: this.onClick.createDelegate(this, ["prev"])
12525         });
12526         //this.addSeparator();
12527         this.add(this.beforePageText);
12528         this.field = Roo.get(this.addDom({
12529            tag: "input",
12530            type: "text",
12531            size: "3",
12532            value: "1",
12533            cls: "x-grid-page-number"
12534         }).el);
12535         this.field.on("keydown", this.onPagingKeydown, this);
12536         this.field.on("focus", function(){this.dom.select();});
12537         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12538         this.field.setHeight(18);
12539         //this.addSeparator();
12540         this.next = this.addButton({
12541             tooltip: this.nextText,
12542             cls: "x-btn-icon x-grid-page-next",
12543             disabled: true,
12544             handler: this.onClick.createDelegate(this, ["next"])
12545         });
12546         this.last = this.addButton({
12547             tooltip: this.lastText,
12548             cls: "x-btn-icon x-grid-page-last",
12549             disabled: true,
12550             handler: this.onClick.createDelegate(this, ["last"])
12551         });
12552         //this.addSeparator();
12553         this.loading = this.addButton({
12554             tooltip: this.refreshText,
12555             cls: "x-btn-icon x-grid-loading",
12556             handler: this.onClick.createDelegate(this, ["refresh"])
12557         });
12558
12559         if(this.displayInfo){
12560             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12561         }
12562     },
12563
12564     // private
12565     updateInfo : function(){
12566         if(this.displayEl){
12567             var count = this.ds.getCount();
12568             var msg = count == 0 ?
12569                 this.emptyMsg :
12570                 String.format(
12571                     this.displayMsg,
12572                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12573                 );
12574             this.displayEl.update(msg);
12575         }
12576     },
12577
12578     // private
12579     onLoad : function(ds, r, o){
12580        this.cursor = o.params ? o.params.start : 0;
12581        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12582
12583        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12584        this.field.dom.value = ap;
12585        this.first.setDisabled(ap == 1);
12586        this.prev.setDisabled(ap == 1);
12587        this.next.setDisabled(ap == ps);
12588        this.last.setDisabled(ap == ps);
12589        this.loading.enable();
12590        this.updateInfo();
12591     },
12592
12593     // private
12594     getPageData : function(){
12595         var total = this.ds.getTotalCount();
12596         return {
12597             total : total,
12598             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12599             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12600         };
12601     },
12602
12603     // private
12604     onLoadError : function(){
12605         this.loading.enable();
12606     },
12607
12608     // private
12609     onPagingKeydown : function(e){
12610         var k = e.getKey();
12611         var d = this.getPageData();
12612         if(k == e.RETURN){
12613             var v = this.field.dom.value, pageNum;
12614             if(!v || isNaN(pageNum = parseInt(v, 10))){
12615                 this.field.dom.value = d.activePage;
12616                 return;
12617             }
12618             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12619             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12620             e.stopEvent();
12621         }
12622         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))
12623         {
12624           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12625           this.field.dom.value = pageNum;
12626           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12627           e.stopEvent();
12628         }
12629         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12630         {
12631           var v = this.field.dom.value, pageNum; 
12632           var increment = (e.shiftKey) ? 10 : 1;
12633           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
12634             increment *= -1;
12635           }
12636           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12637             this.field.dom.value = d.activePage;
12638             return;
12639           }
12640           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12641           {
12642             this.field.dom.value = parseInt(v, 10) + increment;
12643             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12644             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12645           }
12646           e.stopEvent();
12647         }
12648     },
12649
12650     // private
12651     beforeLoad : function(){
12652         if(this.loading){
12653             this.loading.disable();
12654         }
12655     },
12656
12657     // private
12658     onClick : function(which){
12659         var ds = this.ds;
12660         switch(which){
12661             case "first":
12662                 ds.load({params:{start: 0, limit: this.pageSize}});
12663             break;
12664             case "prev":
12665                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12666             break;
12667             case "next":
12668                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12669             break;
12670             case "last":
12671                 var total = ds.getTotalCount();
12672                 var extra = total % this.pageSize;
12673                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12674                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12675             break;
12676             case "refresh":
12677                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12678             break;
12679         }
12680     },
12681
12682     /**
12683      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12684      * @param {Roo.data.Store} store The data store to unbind
12685      */
12686     unbind : function(ds){
12687         ds.un("beforeload", this.beforeLoad, this);
12688         ds.un("load", this.onLoad, this);
12689         ds.un("loadexception", this.onLoadError, this);
12690         ds.un("remove", this.updateInfo, this);
12691         ds.un("add", this.updateInfo, this);
12692         this.ds = undefined;
12693     },
12694
12695     /**
12696      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12697      * @param {Roo.data.Store} store The data store to bind
12698      */
12699     bind : function(ds){
12700         ds.on("beforeload", this.beforeLoad, this);
12701         ds.on("load", this.onLoad, this);
12702         ds.on("loadexception", this.onLoadError, this);
12703         ds.on("remove", this.updateInfo, this);
12704         ds.on("add", this.updateInfo, this);
12705         this.ds = ds;
12706     }
12707 });/*
12708  * Based on:
12709  * Ext JS Library 1.1.1
12710  * Copyright(c) 2006-2007, Ext JS, LLC.
12711  *
12712  * Originally Released Under LGPL - original licence link has changed is not relivant.
12713  *
12714  * Fork - LGPL
12715  * <script type="text/javascript">
12716  */
12717
12718 /**
12719  * @class Roo.Resizable
12720  * @extends Roo.util.Observable
12721  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12722  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12723  * 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
12724  * the element will be wrapped for you automatically.</p>
12725  * <p>Here is the list of valid resize handles:</p>
12726  * <pre>
12727 Value   Description
12728 ------  -------------------
12729  'n'     north
12730  's'     south
12731  'e'     east
12732  'w'     west
12733  'nw'    northwest
12734  'sw'    southwest
12735  'se'    southeast
12736  'ne'    northeast
12737  'hd'    horizontal drag
12738  'all'   all
12739 </pre>
12740  * <p>Here's an example showing the creation of a typical Resizable:</p>
12741  * <pre><code>
12742 var resizer = new Roo.Resizable("element-id", {
12743     handles: 'all',
12744     minWidth: 200,
12745     minHeight: 100,
12746     maxWidth: 500,
12747     maxHeight: 400,
12748     pinned: true
12749 });
12750 resizer.on("resize", myHandler);
12751 </code></pre>
12752  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12753  * resizer.east.setDisplayed(false);</p>
12754  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12755  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12756  * resize operation's new size (defaults to [0, 0])
12757  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12758  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12759  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12760  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12761  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12762  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12763  * @cfg {Number} width The width of the element in pixels (defaults to null)
12764  * @cfg {Number} height The height of the element in pixels (defaults to null)
12765  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12766  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12767  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12768  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12769  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12770  * in favor of the handles config option (defaults to false)
12771  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12772  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12773  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12774  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12775  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12776  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12777  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12778  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12779  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12780  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12781  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12782  * @constructor
12783  * Create a new resizable component
12784  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12785  * @param {Object} config configuration options
12786   */
12787 Roo.Resizable = function(el, config)
12788 {
12789     this.el = Roo.get(el);
12790
12791     if(config && config.wrap){
12792         config.resizeChild = this.el;
12793         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12794         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12795         this.el.setStyle("overflow", "hidden");
12796         this.el.setPositioning(config.resizeChild.getPositioning());
12797         config.resizeChild.clearPositioning();
12798         if(!config.width || !config.height){
12799             var csize = config.resizeChild.getSize();
12800             this.el.setSize(csize.width, csize.height);
12801         }
12802         if(config.pinned && !config.adjustments){
12803             config.adjustments = "auto";
12804         }
12805     }
12806
12807     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12808     this.proxy.unselectable();
12809     this.proxy.enableDisplayMode('block');
12810
12811     Roo.apply(this, config);
12812
12813     if(this.pinned){
12814         this.disableTrackOver = true;
12815         this.el.addClass("x-resizable-pinned");
12816     }
12817     // if the element isn't positioned, make it relative
12818     var position = this.el.getStyle("position");
12819     if(position != "absolute" && position != "fixed"){
12820         this.el.setStyle("position", "relative");
12821     }
12822     if(!this.handles){ // no handles passed, must be legacy style
12823         this.handles = 's,e,se';
12824         if(this.multiDirectional){
12825             this.handles += ',n,w';
12826         }
12827     }
12828     if(this.handles == "all"){
12829         this.handles = "n s e w ne nw se sw";
12830     }
12831     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12832     var ps = Roo.Resizable.positions;
12833     for(var i = 0, len = hs.length; i < len; i++){
12834         if(hs[i] && ps[hs[i]]){
12835             var pos = ps[hs[i]];
12836             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12837         }
12838     }
12839     // legacy
12840     this.corner = this.southeast;
12841     
12842     // updateBox = the box can move..
12843     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12844         this.updateBox = true;
12845     }
12846
12847     this.activeHandle = null;
12848
12849     if(this.resizeChild){
12850         if(typeof this.resizeChild == "boolean"){
12851             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12852         }else{
12853             this.resizeChild = Roo.get(this.resizeChild, true);
12854         }
12855     }
12856     
12857     if(this.adjustments == "auto"){
12858         var rc = this.resizeChild;
12859         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12860         if(rc && (hw || hn)){
12861             rc.position("relative");
12862             rc.setLeft(hw ? hw.el.getWidth() : 0);
12863             rc.setTop(hn ? hn.el.getHeight() : 0);
12864         }
12865         this.adjustments = [
12866             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12867             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12868         ];
12869     }
12870
12871     if(this.draggable){
12872         this.dd = this.dynamic ?
12873             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12874         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12875     }
12876
12877     // public events
12878     this.addEvents({
12879         /**
12880          * @event beforeresize
12881          * Fired before resize is allowed. Set enabled to false to cancel resize.
12882          * @param {Roo.Resizable} this
12883          * @param {Roo.EventObject} e The mousedown event
12884          */
12885         "beforeresize" : true,
12886         /**
12887          * @event resizing
12888          * Fired a resizing.
12889          * @param {Roo.Resizable} this
12890          * @param {Number} x The new x position
12891          * @param {Number} y The new y position
12892          * @param {Number} w The new w width
12893          * @param {Number} h The new h hight
12894          * @param {Roo.EventObject} e The mouseup event
12895          */
12896         "resizing" : true,
12897         /**
12898          * @event resize
12899          * Fired after a resize.
12900          * @param {Roo.Resizable} this
12901          * @param {Number} width The new width
12902          * @param {Number} height The new height
12903          * @param {Roo.EventObject} e The mouseup event
12904          */
12905         "resize" : true
12906     });
12907
12908     if(this.width !== null && this.height !== null){
12909         this.resizeTo(this.width, this.height);
12910     }else{
12911         this.updateChildSize();
12912     }
12913     if(Roo.isIE){
12914         this.el.dom.style.zoom = 1;
12915     }
12916     Roo.Resizable.superclass.constructor.call(this);
12917 };
12918
12919 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12920         resizeChild : false,
12921         adjustments : [0, 0],
12922         minWidth : 5,
12923         minHeight : 5,
12924         maxWidth : 10000,
12925         maxHeight : 10000,
12926         enabled : true,
12927         animate : false,
12928         duration : .35,
12929         dynamic : false,
12930         handles : false,
12931         multiDirectional : false,
12932         disableTrackOver : false,
12933         easing : 'easeOutStrong',
12934         widthIncrement : 0,
12935         heightIncrement : 0,
12936         pinned : false,
12937         width : null,
12938         height : null,
12939         preserveRatio : false,
12940         transparent: false,
12941         minX: 0,
12942         minY: 0,
12943         draggable: false,
12944
12945         /**
12946          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12947          */
12948         constrainTo: undefined,
12949         /**
12950          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12951          */
12952         resizeRegion: undefined,
12953
12954
12955     /**
12956      * Perform a manual resize
12957      * @param {Number} width
12958      * @param {Number} height
12959      */
12960     resizeTo : function(width, height){
12961         this.el.setSize(width, height);
12962         this.updateChildSize();
12963         this.fireEvent("resize", this, width, height, null);
12964     },
12965
12966     // private
12967     startSizing : function(e, handle){
12968         this.fireEvent("beforeresize", this, e);
12969         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12970
12971             if(!this.overlay){
12972                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12973                 this.overlay.unselectable();
12974                 this.overlay.enableDisplayMode("block");
12975                 this.overlay.on("mousemove", this.onMouseMove, this);
12976                 this.overlay.on("mouseup", this.onMouseUp, this);
12977             }
12978             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12979
12980             this.resizing = true;
12981             this.startBox = this.el.getBox();
12982             this.startPoint = e.getXY();
12983             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12984                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12985
12986             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12987             this.overlay.show();
12988
12989             if(this.constrainTo) {
12990                 var ct = Roo.get(this.constrainTo);
12991                 this.resizeRegion = ct.getRegion().adjust(
12992                     ct.getFrameWidth('t'),
12993                     ct.getFrameWidth('l'),
12994                     -ct.getFrameWidth('b'),
12995                     -ct.getFrameWidth('r')
12996                 );
12997             }
12998
12999             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13000             this.proxy.show();
13001             this.proxy.setBox(this.startBox);
13002             if(!this.dynamic){
13003                 this.proxy.setStyle('visibility', 'visible');
13004             }
13005         }
13006     },
13007
13008     // private
13009     onMouseDown : function(handle, e){
13010         if(this.enabled){
13011             e.stopEvent();
13012             this.activeHandle = handle;
13013             this.startSizing(e, handle);
13014         }
13015     },
13016
13017     // private
13018     onMouseUp : function(e){
13019         var size = this.resizeElement();
13020         this.resizing = false;
13021         this.handleOut();
13022         this.overlay.hide();
13023         this.proxy.hide();
13024         this.fireEvent("resize", this, size.width, size.height, e);
13025     },
13026
13027     // private
13028     updateChildSize : function(){
13029         
13030         if(this.resizeChild){
13031             var el = this.el;
13032             var child = this.resizeChild;
13033             var adj = this.adjustments;
13034             if(el.dom.offsetWidth){
13035                 var b = el.getSize(true);
13036                 child.setSize(b.width+adj[0], b.height+adj[1]);
13037             }
13038             // Second call here for IE
13039             // The first call enables instant resizing and
13040             // the second call corrects scroll bars if they
13041             // exist
13042             if(Roo.isIE){
13043                 setTimeout(function(){
13044                     if(el.dom.offsetWidth){
13045                         var b = el.getSize(true);
13046                         child.setSize(b.width+adj[0], b.height+adj[1]);
13047                     }
13048                 }, 10);
13049             }
13050         }
13051     },
13052
13053     // private
13054     snap : function(value, inc, min){
13055         if(!inc || !value) {
13056             return value;
13057         }
13058         var newValue = value;
13059         var m = value % inc;
13060         if(m > 0){
13061             if(m > (inc/2)){
13062                 newValue = value + (inc-m);
13063             }else{
13064                 newValue = value - m;
13065             }
13066         }
13067         return Math.max(min, newValue);
13068     },
13069
13070     // private
13071     resizeElement : function(){
13072         var box = this.proxy.getBox();
13073         if(this.updateBox){
13074             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13075         }else{
13076             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13077         }
13078         this.updateChildSize();
13079         if(!this.dynamic){
13080             this.proxy.hide();
13081         }
13082         return box;
13083     },
13084
13085     // private
13086     constrain : function(v, diff, m, mx){
13087         if(v - diff < m){
13088             diff = v - m;
13089         }else if(v - diff > mx){
13090             diff = mx - v;
13091         }
13092         return diff;
13093     },
13094
13095     // private
13096     onMouseMove : function(e){
13097         
13098         if(this.enabled){
13099             try{// try catch so if something goes wrong the user doesn't get hung
13100
13101             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13102                 return;
13103             }
13104
13105             //var curXY = this.startPoint;
13106             var curSize = this.curSize || this.startBox;
13107             var x = this.startBox.x, y = this.startBox.y;
13108             var ox = x, oy = y;
13109             var w = curSize.width, h = curSize.height;
13110             var ow = w, oh = h;
13111             var mw = this.minWidth, mh = this.minHeight;
13112             var mxw = this.maxWidth, mxh = this.maxHeight;
13113             var wi = this.widthIncrement;
13114             var hi = this.heightIncrement;
13115
13116             var eventXY = e.getXY();
13117             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13118             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13119
13120             var pos = this.activeHandle.position;
13121
13122             switch(pos){
13123                 case "east":
13124                     w += diffX;
13125                     w = Math.min(Math.max(mw, w), mxw);
13126                     break;
13127              
13128                 case "south":
13129                     h += diffY;
13130                     h = Math.min(Math.max(mh, h), mxh);
13131                     break;
13132                 case "southeast":
13133                     w += diffX;
13134                     h += diffY;
13135                     w = Math.min(Math.max(mw, w), mxw);
13136                     h = Math.min(Math.max(mh, h), mxh);
13137                     break;
13138                 case "north":
13139                     diffY = this.constrain(h, diffY, mh, mxh);
13140                     y += diffY;
13141                     h -= diffY;
13142                     break;
13143                 case "hdrag":
13144                     
13145                     if (wi) {
13146                         var adiffX = Math.abs(diffX);
13147                         var sub = (adiffX % wi); // how much 
13148                         if (sub > (wi/2)) { // far enough to snap
13149                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13150                         } else {
13151                             // remove difference.. 
13152                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13153                         }
13154                     }
13155                     x += diffX;
13156                     x = Math.max(this.minX, x);
13157                     break;
13158                 case "west":
13159                     diffX = this.constrain(w, diffX, mw, mxw);
13160                     x += diffX;
13161                     w -= diffX;
13162                     break;
13163                 case "northeast":
13164                     w += diffX;
13165                     w = Math.min(Math.max(mw, w), mxw);
13166                     diffY = this.constrain(h, diffY, mh, mxh);
13167                     y += diffY;
13168                     h -= diffY;
13169                     break;
13170                 case "northwest":
13171                     diffX = this.constrain(w, diffX, mw, mxw);
13172                     diffY = this.constrain(h, diffY, mh, mxh);
13173                     y += diffY;
13174                     h -= diffY;
13175                     x += diffX;
13176                     w -= diffX;
13177                     break;
13178                case "southwest":
13179                     diffX = this.constrain(w, diffX, mw, mxw);
13180                     h += diffY;
13181                     h = Math.min(Math.max(mh, h), mxh);
13182                     x += diffX;
13183                     w -= diffX;
13184                     break;
13185             }
13186
13187             var sw = this.snap(w, wi, mw);
13188             var sh = this.snap(h, hi, mh);
13189             if(sw != w || sh != h){
13190                 switch(pos){
13191                     case "northeast":
13192                         y -= sh - h;
13193                     break;
13194                     case "north":
13195                         y -= sh - h;
13196                         break;
13197                     case "southwest":
13198                         x -= sw - w;
13199                     break;
13200                     case "west":
13201                         x -= sw - w;
13202                         break;
13203                     case "northwest":
13204                         x -= sw - w;
13205                         y -= sh - h;
13206                     break;
13207                 }
13208                 w = sw;
13209                 h = sh;
13210             }
13211
13212             if(this.preserveRatio){
13213                 switch(pos){
13214                     case "southeast":
13215                     case "east":
13216                         h = oh * (w/ow);
13217                         h = Math.min(Math.max(mh, h), mxh);
13218                         w = ow * (h/oh);
13219                        break;
13220                     case "south":
13221                         w = ow * (h/oh);
13222                         w = Math.min(Math.max(mw, w), mxw);
13223                         h = oh * (w/ow);
13224                         break;
13225                     case "northeast":
13226                         w = ow * (h/oh);
13227                         w = Math.min(Math.max(mw, w), mxw);
13228                         h = oh * (w/ow);
13229                     break;
13230                     case "north":
13231                         var tw = w;
13232                         w = ow * (h/oh);
13233                         w = Math.min(Math.max(mw, w), mxw);
13234                         h = oh * (w/ow);
13235                         x += (tw - w) / 2;
13236                         break;
13237                     case "southwest":
13238                         h = oh * (w/ow);
13239                         h = Math.min(Math.max(mh, h), mxh);
13240                         var tw = w;
13241                         w = ow * (h/oh);
13242                         x += tw - w;
13243                         break;
13244                     case "west":
13245                         var th = h;
13246                         h = oh * (w/ow);
13247                         h = Math.min(Math.max(mh, h), mxh);
13248                         y += (th - h) / 2;
13249                         var tw = w;
13250                         w = ow * (h/oh);
13251                         x += tw - w;
13252                        break;
13253                     case "northwest":
13254                         var tw = w;
13255                         var th = h;
13256                         h = oh * (w/ow);
13257                         h = Math.min(Math.max(mh, h), mxh);
13258                         w = ow * (h/oh);
13259                         y += th - h;
13260                         x += tw - w;
13261                        break;
13262
13263                 }
13264             }
13265             if (pos == 'hdrag') {
13266                 w = ow;
13267             }
13268             this.proxy.setBounds(x, y, w, h);
13269             if(this.dynamic){
13270                 this.resizeElement();
13271             }
13272             }catch(e){}
13273         }
13274         this.fireEvent("resizing", this, x, y, w, h, e);
13275     },
13276
13277     // private
13278     handleOver : function(){
13279         if(this.enabled){
13280             this.el.addClass("x-resizable-over");
13281         }
13282     },
13283
13284     // private
13285     handleOut : function(){
13286         if(!this.resizing){
13287             this.el.removeClass("x-resizable-over");
13288         }
13289     },
13290
13291     /**
13292      * Returns the element this component is bound to.
13293      * @return {Roo.Element}
13294      */
13295     getEl : function(){
13296         return this.el;
13297     },
13298
13299     /**
13300      * Returns the resizeChild element (or null).
13301      * @return {Roo.Element}
13302      */
13303     getResizeChild : function(){
13304         return this.resizeChild;
13305     },
13306     groupHandler : function()
13307     {
13308         
13309     },
13310     /**
13311      * Destroys this resizable. If the element was wrapped and
13312      * removeEl is not true then the element remains.
13313      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13314      */
13315     destroy : function(removeEl){
13316         this.proxy.remove();
13317         if(this.overlay){
13318             this.overlay.removeAllListeners();
13319             this.overlay.remove();
13320         }
13321         var ps = Roo.Resizable.positions;
13322         for(var k in ps){
13323             if(typeof ps[k] != "function" && this[ps[k]]){
13324                 var h = this[ps[k]];
13325                 h.el.removeAllListeners();
13326                 h.el.remove();
13327             }
13328         }
13329         if(removeEl){
13330             this.el.update("");
13331             this.el.remove();
13332         }
13333     }
13334 });
13335
13336 // private
13337 // hash to map config positions to true positions
13338 Roo.Resizable.positions = {
13339     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13340     hd: "hdrag"
13341 };
13342
13343 // private
13344 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13345     if(!this.tpl){
13346         // only initialize the template if resizable is used
13347         var tpl = Roo.DomHelper.createTemplate(
13348             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13349         );
13350         tpl.compile();
13351         Roo.Resizable.Handle.prototype.tpl = tpl;
13352     }
13353     this.position = pos;
13354     this.rz = rz;
13355     // show north drag fro topdra
13356     var handlepos = pos == 'hdrag' ? 'north' : pos;
13357     
13358     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13359     if (pos == 'hdrag') {
13360         this.el.setStyle('cursor', 'pointer');
13361     }
13362     this.el.unselectable();
13363     if(transparent){
13364         this.el.setOpacity(0);
13365     }
13366     this.el.on("mousedown", this.onMouseDown, this);
13367     if(!disableTrackOver){
13368         this.el.on("mouseover", this.onMouseOver, this);
13369         this.el.on("mouseout", this.onMouseOut, this);
13370     }
13371 };
13372
13373 // private
13374 Roo.Resizable.Handle.prototype = {
13375     afterResize : function(rz){
13376         Roo.log('after?');
13377         // do nothing
13378     },
13379     // private
13380     onMouseDown : function(e){
13381         this.rz.onMouseDown(this, e);
13382     },
13383     // private
13384     onMouseOver : function(e){
13385         this.rz.handleOver(this, e);
13386     },
13387     // private
13388     onMouseOut : function(e){
13389         this.rz.handleOut(this, e);
13390     }
13391 };/*
13392  * Based on:
13393  * Ext JS Library 1.1.1
13394  * Copyright(c) 2006-2007, Ext JS, LLC.
13395  *
13396  * Originally Released Under LGPL - original licence link has changed is not relivant.
13397  *
13398  * Fork - LGPL
13399  * <script type="text/javascript">
13400  */
13401
13402 /**
13403  * @class Roo.Editor
13404  * @extends Roo.Component
13405  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13406  * @constructor
13407  * Create a new Editor
13408  * @param {Roo.form.Field} field The Field object (or descendant)
13409  * @param {Object} config The config object
13410  */
13411 Roo.Editor = function(field, config){
13412     Roo.Editor.superclass.constructor.call(this, config);
13413     this.field = field;
13414     this.addEvents({
13415         /**
13416              * @event beforestartedit
13417              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13418              * false from the handler of this event.
13419              * @param {Editor} this
13420              * @param {Roo.Element} boundEl The underlying element bound to this editor
13421              * @param {Mixed} value The field value being set
13422              */
13423         "beforestartedit" : true,
13424         /**
13425              * @event startedit
13426              * Fires when this editor is displayed
13427              * @param {Roo.Element} boundEl The underlying element bound to this editor
13428              * @param {Mixed} value The starting field value
13429              */
13430         "startedit" : true,
13431         /**
13432              * @event beforecomplete
13433              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13434              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13435              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13436              * event will not fire since no edit actually occurred.
13437              * @param {Editor} this
13438              * @param {Mixed} value The current field value
13439              * @param {Mixed} startValue The original field value
13440              */
13441         "beforecomplete" : true,
13442         /**
13443              * @event complete
13444              * Fires after editing is complete and any changed value has been written to the underlying field.
13445              * @param {Editor} this
13446              * @param {Mixed} value The current field value
13447              * @param {Mixed} startValue The original field value
13448              */
13449         "complete" : true,
13450         /**
13451          * @event specialkey
13452          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13453          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13454          * @param {Roo.form.Field} this
13455          * @param {Roo.EventObject} e The event object
13456          */
13457         "specialkey" : true
13458     });
13459 };
13460
13461 Roo.extend(Roo.Editor, Roo.Component, {
13462     /**
13463      * @cfg {Boolean/String} autosize
13464      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13465      * or "height" to adopt the height only (defaults to false)
13466      */
13467     /**
13468      * @cfg {Boolean} revertInvalid
13469      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13470      * validation fails (defaults to true)
13471      */
13472     /**
13473      * @cfg {Boolean} ignoreNoChange
13474      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13475      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13476      * will never be ignored.
13477      */
13478     /**
13479      * @cfg {Boolean} hideEl
13480      * False to keep the bound element visible while the editor is displayed (defaults to true)
13481      */
13482     /**
13483      * @cfg {Mixed} value
13484      * The data value of the underlying field (defaults to "")
13485      */
13486     value : "",
13487     /**
13488      * @cfg {String} alignment
13489      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13490      */
13491     alignment: "c-c?",
13492     /**
13493      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13494      * for bottom-right shadow (defaults to "frame")
13495      */
13496     shadow : "frame",
13497     /**
13498      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13499      */
13500     constrain : false,
13501     /**
13502      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13503      */
13504     completeOnEnter : false,
13505     /**
13506      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13507      */
13508     cancelOnEsc : false,
13509     /**
13510      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13511      */
13512     updateEl : false,
13513
13514     // private
13515     onRender : function(ct, position){
13516         this.el = new Roo.Layer({
13517             shadow: this.shadow,
13518             cls: "x-editor",
13519             parentEl : ct,
13520             shim : this.shim,
13521             shadowOffset:4,
13522             id: this.id,
13523             constrain: this.constrain
13524         });
13525         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13526         if(this.field.msgTarget != 'title'){
13527             this.field.msgTarget = 'qtip';
13528         }
13529         this.field.render(this.el);
13530         if(Roo.isGecko){
13531             this.field.el.dom.setAttribute('autocomplete', 'off');
13532         }
13533         this.field.on("specialkey", this.onSpecialKey, this);
13534         if(this.swallowKeys){
13535             this.field.el.swallowEvent(['keydown','keypress']);
13536         }
13537         this.field.show();
13538         this.field.on("blur", this.onBlur, this);
13539         if(this.field.grow){
13540             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13541         }
13542     },
13543
13544     onSpecialKey : function(field, e)
13545     {
13546         //Roo.log('editor onSpecialKey');
13547         if(this.completeOnEnter && e.getKey() == e.ENTER){
13548             e.stopEvent();
13549             this.completeEdit();
13550             return;
13551         }
13552         // do not fire special key otherwise it might hide close the editor...
13553         if(e.getKey() == e.ENTER){    
13554             return;
13555         }
13556         if(this.cancelOnEsc && e.getKey() == e.ESC){
13557             this.cancelEdit();
13558             return;
13559         } 
13560         this.fireEvent('specialkey', field, e);
13561     
13562     },
13563
13564     /**
13565      * Starts the editing process and shows the editor.
13566      * @param {String/HTMLElement/Element} el The element to edit
13567      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13568       * to the innerHTML of el.
13569      */
13570     startEdit : function(el, value){
13571         if(this.editing){
13572             this.completeEdit();
13573         }
13574         this.boundEl = Roo.get(el);
13575         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13576         if(!this.rendered){
13577             this.render(this.parentEl || document.body);
13578         }
13579         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13580             return;
13581         }
13582         this.startValue = v;
13583         this.field.setValue(v);
13584         if(this.autoSize){
13585             var sz = this.boundEl.getSize();
13586             switch(this.autoSize){
13587                 case "width":
13588                 this.setSize(sz.width,  "");
13589                 break;
13590                 case "height":
13591                 this.setSize("",  sz.height);
13592                 break;
13593                 default:
13594                 this.setSize(sz.width,  sz.height);
13595             }
13596         }
13597         this.el.alignTo(this.boundEl, this.alignment);
13598         this.editing = true;
13599         if(Roo.QuickTips){
13600             Roo.QuickTips.disable();
13601         }
13602         this.show();
13603     },
13604
13605     /**
13606      * Sets the height and width of this editor.
13607      * @param {Number} width The new width
13608      * @param {Number} height The new height
13609      */
13610     setSize : function(w, h){
13611         this.field.setSize(w, h);
13612         if(this.el){
13613             this.el.sync();
13614         }
13615     },
13616
13617     /**
13618      * Realigns the editor to the bound field based on the current alignment config value.
13619      */
13620     realign : function(){
13621         this.el.alignTo(this.boundEl, this.alignment);
13622     },
13623
13624     /**
13625      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13626      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13627      */
13628     completeEdit : function(remainVisible){
13629         if(!this.editing){
13630             return;
13631         }
13632         var v = this.getValue();
13633         if(this.revertInvalid !== false && !this.field.isValid()){
13634             v = this.startValue;
13635             this.cancelEdit(true);
13636         }
13637         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13638             this.editing = false;
13639             this.hide();
13640             return;
13641         }
13642         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13643             this.editing = false;
13644             if(this.updateEl && this.boundEl){
13645                 this.boundEl.update(v);
13646             }
13647             if(remainVisible !== true){
13648                 this.hide();
13649             }
13650             this.fireEvent("complete", this, v, this.startValue);
13651         }
13652     },
13653
13654     // private
13655     onShow : function(){
13656         this.el.show();
13657         if(this.hideEl !== false){
13658             this.boundEl.hide();
13659         }
13660         this.field.show();
13661         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13662             this.fixIEFocus = true;
13663             this.deferredFocus.defer(50, this);
13664         }else{
13665             this.field.focus();
13666         }
13667         this.fireEvent("startedit", this.boundEl, this.startValue);
13668     },
13669
13670     deferredFocus : function(){
13671         if(this.editing){
13672             this.field.focus();
13673         }
13674     },
13675
13676     /**
13677      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13678      * reverted to the original starting value.
13679      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13680      * cancel (defaults to false)
13681      */
13682     cancelEdit : function(remainVisible){
13683         if(this.editing){
13684             this.setValue(this.startValue);
13685             if(remainVisible !== true){
13686                 this.hide();
13687             }
13688         }
13689     },
13690
13691     // private
13692     onBlur : function(){
13693         if(this.allowBlur !== true && this.editing){
13694             this.completeEdit();
13695         }
13696     },
13697
13698     // private
13699     onHide : function(){
13700         if(this.editing){
13701             this.completeEdit();
13702             return;
13703         }
13704         this.field.blur();
13705         if(this.field.collapse){
13706             this.field.collapse();
13707         }
13708         this.el.hide();
13709         if(this.hideEl !== false){
13710             this.boundEl.show();
13711         }
13712         if(Roo.QuickTips){
13713             Roo.QuickTips.enable();
13714         }
13715     },
13716
13717     /**
13718      * Sets the data value of the editor
13719      * @param {Mixed} value Any valid value supported by the underlying field
13720      */
13721     setValue : function(v){
13722         this.field.setValue(v);
13723     },
13724
13725     /**
13726      * Gets the data value of the editor
13727      * @return {Mixed} The data value
13728      */
13729     getValue : function(){
13730         return this.field.getValue();
13731     }
13732 });/*
13733  * Based on:
13734  * Ext JS Library 1.1.1
13735  * Copyright(c) 2006-2007, Ext JS, LLC.
13736  *
13737  * Originally Released Under LGPL - original licence link has changed is not relivant.
13738  *
13739  * Fork - LGPL
13740  * <script type="text/javascript">
13741  */
13742  
13743 /**
13744  * @class Roo.BasicDialog
13745  * @extends Roo.util.Observable
13746  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13747  * <pre><code>
13748 var dlg = new Roo.BasicDialog("my-dlg", {
13749     height: 200,
13750     width: 300,
13751     minHeight: 100,
13752     minWidth: 150,
13753     modal: true,
13754     proxyDrag: true,
13755     shadow: true
13756 });
13757 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13758 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13759 dlg.addButton('Cancel', dlg.hide, dlg);
13760 dlg.show();
13761 </code></pre>
13762   <b>A Dialog should always be a direct child of the body element.</b>
13763  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13764  * @cfg {String} title Default text to display in the title bar (defaults to null)
13765  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13766  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13767  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13768  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13769  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13770  * (defaults to null with no animation)
13771  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13772  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13773  * property for valid values (defaults to 'all')
13774  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13775  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13776  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13777  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13778  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13779  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13780  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13781  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13782  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13783  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13784  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13785  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13786  * draggable = true (defaults to false)
13787  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13788  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13789  * shadow (defaults to false)
13790  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13791  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13792  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13793  * @cfg {Array} buttons Array of buttons
13794  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13795  * @constructor
13796  * Create a new BasicDialog.
13797  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13798  * @param {Object} config Configuration options
13799  */
13800 Roo.BasicDialog = function(el, config){
13801     this.el = Roo.get(el);
13802     var dh = Roo.DomHelper;
13803     if(!this.el && config && config.autoCreate){
13804         if(typeof config.autoCreate == "object"){
13805             if(!config.autoCreate.id){
13806                 config.autoCreate.id = el;
13807             }
13808             this.el = dh.append(document.body,
13809                         config.autoCreate, true);
13810         }else{
13811             this.el = dh.append(document.body,
13812                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13813         }
13814     }
13815     el = this.el;
13816     el.setDisplayed(true);
13817     el.hide = this.hideAction;
13818     this.id = el.id;
13819     el.addClass("x-dlg");
13820
13821     Roo.apply(this, config);
13822
13823     this.proxy = el.createProxy("x-dlg-proxy");
13824     this.proxy.hide = this.hideAction;
13825     this.proxy.setOpacity(.5);
13826     this.proxy.hide();
13827
13828     if(config.width){
13829         el.setWidth(config.width);
13830     }
13831     if(config.height){
13832         el.setHeight(config.height);
13833     }
13834     this.size = el.getSize();
13835     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13836         this.xy = [config.x,config.y];
13837     }else{
13838         this.xy = el.getCenterXY(true);
13839     }
13840     /** The header element @type Roo.Element */
13841     this.header = el.child("> .x-dlg-hd");
13842     /** The body element @type Roo.Element */
13843     this.body = el.child("> .x-dlg-bd");
13844     /** The footer element @type Roo.Element */
13845     this.footer = el.child("> .x-dlg-ft");
13846
13847     if(!this.header){
13848         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13849     }
13850     if(!this.body){
13851         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13852     }
13853
13854     this.header.unselectable();
13855     if(this.title){
13856         this.header.update(this.title);
13857     }
13858     // this element allows the dialog to be focused for keyboard event
13859     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13860     this.focusEl.swallowEvent("click", true);
13861
13862     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13863
13864     // wrap the body and footer for special rendering
13865     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13866     if(this.footer){
13867         this.bwrap.dom.appendChild(this.footer.dom);
13868     }
13869
13870     this.bg = this.el.createChild({
13871         tag: "div", cls:"x-dlg-bg",
13872         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13873     });
13874     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13875
13876
13877     if(this.autoScroll !== false && !this.autoTabs){
13878         this.body.setStyle("overflow", "auto");
13879     }
13880
13881     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13882
13883     if(this.closable !== false){
13884         this.el.addClass("x-dlg-closable");
13885         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13886         this.close.on("click", this.closeClick, this);
13887         this.close.addClassOnOver("x-dlg-close-over");
13888     }
13889     if(this.collapsible !== false){
13890         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13891         this.collapseBtn.on("click", this.collapseClick, this);
13892         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13893         this.header.on("dblclick", this.collapseClick, this);
13894     }
13895     if(this.resizable !== false){
13896         this.el.addClass("x-dlg-resizable");
13897         this.resizer = new Roo.Resizable(el, {
13898             minWidth: this.minWidth || 80,
13899             minHeight:this.minHeight || 80,
13900             handles: this.resizeHandles || "all",
13901             pinned: true
13902         });
13903         this.resizer.on("beforeresize", this.beforeResize, this);
13904         this.resizer.on("resize", this.onResize, this);
13905     }
13906     if(this.draggable !== false){
13907         el.addClass("x-dlg-draggable");
13908         if (!this.proxyDrag) {
13909             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13910         }
13911         else {
13912             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13913         }
13914         dd.setHandleElId(this.header.id);
13915         dd.endDrag = this.endMove.createDelegate(this);
13916         dd.startDrag = this.startMove.createDelegate(this);
13917         dd.onDrag = this.onDrag.createDelegate(this);
13918         dd.scroll = false;
13919         this.dd = dd;
13920     }
13921     if(this.modal){
13922         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13923         this.mask.enableDisplayMode("block");
13924         this.mask.hide();
13925         this.el.addClass("x-dlg-modal");
13926     }
13927     if(this.shadow){
13928         this.shadow = new Roo.Shadow({
13929             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13930             offset : this.shadowOffset
13931         });
13932     }else{
13933         this.shadowOffset = 0;
13934     }
13935     if(Roo.useShims && this.shim !== false){
13936         this.shim = this.el.createShim();
13937         this.shim.hide = this.hideAction;
13938         this.shim.hide();
13939     }else{
13940         this.shim = false;
13941     }
13942     if(this.autoTabs){
13943         this.initTabs();
13944     }
13945     if (this.buttons) { 
13946         var bts= this.buttons;
13947         this.buttons = [];
13948         Roo.each(bts, function(b) {
13949             this.addButton(b);
13950         }, this);
13951     }
13952     
13953     
13954     this.addEvents({
13955         /**
13956          * @event keydown
13957          * Fires when a key is pressed
13958          * @param {Roo.BasicDialog} this
13959          * @param {Roo.EventObject} e
13960          */
13961         "keydown" : true,
13962         /**
13963          * @event move
13964          * Fires when this dialog is moved by the user.
13965          * @param {Roo.BasicDialog} this
13966          * @param {Number} x The new page X
13967          * @param {Number} y The new page Y
13968          */
13969         "move" : true,
13970         /**
13971          * @event resize
13972          * Fires when this dialog is resized by the user.
13973          * @param {Roo.BasicDialog} this
13974          * @param {Number} width The new width
13975          * @param {Number} height The new height
13976          */
13977         "resize" : true,
13978         /**
13979          * @event beforehide
13980          * Fires before this dialog is hidden.
13981          * @param {Roo.BasicDialog} this
13982          */
13983         "beforehide" : true,
13984         /**
13985          * @event hide
13986          * Fires when this dialog is hidden.
13987          * @param {Roo.BasicDialog} this
13988          */
13989         "hide" : true,
13990         /**
13991          * @event beforeshow
13992          * Fires before this dialog is shown.
13993          * @param {Roo.BasicDialog} this
13994          */
13995         "beforeshow" : true,
13996         /**
13997          * @event show
13998          * Fires when this dialog is shown.
13999          * @param {Roo.BasicDialog} this
14000          */
14001         "show" : true
14002     });
14003     el.on("keydown", this.onKeyDown, this);
14004     el.on("mousedown", this.toFront, this);
14005     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14006     this.el.hide();
14007     Roo.DialogManager.register(this);
14008     Roo.BasicDialog.superclass.constructor.call(this);
14009 };
14010
14011 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14012     shadowOffset: Roo.isIE ? 6 : 5,
14013     minHeight: 80,
14014     minWidth: 200,
14015     minButtonWidth: 75,
14016     defaultButton: null,
14017     buttonAlign: "right",
14018     tabTag: 'div',
14019     firstShow: true,
14020
14021     /**
14022      * Sets the dialog title text
14023      * @param {String} text The title text to display
14024      * @return {Roo.BasicDialog} this
14025      */
14026     setTitle : function(text){
14027         this.header.update(text);
14028         return this;
14029     },
14030
14031     // private
14032     closeClick : function(){
14033         this.hide();
14034     },
14035
14036     // private
14037     collapseClick : function(){
14038         this[this.collapsed ? "expand" : "collapse"]();
14039     },
14040
14041     /**
14042      * Collapses the dialog to its minimized state (only the title bar is visible).
14043      * Equivalent to the user clicking the collapse dialog button.
14044      */
14045     collapse : function(){
14046         if(!this.collapsed){
14047             this.collapsed = true;
14048             this.el.addClass("x-dlg-collapsed");
14049             this.restoreHeight = this.el.getHeight();
14050             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14051         }
14052     },
14053
14054     /**
14055      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14056      * clicking the expand dialog button.
14057      */
14058     expand : function(){
14059         if(this.collapsed){
14060             this.collapsed = false;
14061             this.el.removeClass("x-dlg-collapsed");
14062             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14063         }
14064     },
14065
14066     /**
14067      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14068      * @return {Roo.TabPanel} The tabs component
14069      */
14070     initTabs : function(){
14071         var tabs = this.getTabs();
14072         while(tabs.getTab(0)){
14073             tabs.removeTab(0);
14074         }
14075         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14076             var dom = el.dom;
14077             tabs.addTab(Roo.id(dom), dom.title);
14078             dom.title = "";
14079         });
14080         tabs.activate(0);
14081         return tabs;
14082     },
14083
14084     // private
14085     beforeResize : function(){
14086         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14087     },
14088
14089     // private
14090     onResize : function(){
14091         this.refreshSize();
14092         this.syncBodyHeight();
14093         this.adjustAssets();
14094         this.focus();
14095         this.fireEvent("resize", this, this.size.width, this.size.height);
14096     },
14097
14098     // private
14099     onKeyDown : function(e){
14100         if(this.isVisible()){
14101             this.fireEvent("keydown", this, e);
14102         }
14103     },
14104
14105     /**
14106      * Resizes the dialog.
14107      * @param {Number} width
14108      * @param {Number} height
14109      * @return {Roo.BasicDialog} this
14110      */
14111     resizeTo : function(width, height){
14112         this.el.setSize(width, height);
14113         this.size = {width: width, height: height};
14114         this.syncBodyHeight();
14115         if(this.fixedcenter){
14116             this.center();
14117         }
14118         if(this.isVisible()){
14119             this.constrainXY();
14120             this.adjustAssets();
14121         }
14122         this.fireEvent("resize", this, width, height);
14123         return this;
14124     },
14125
14126
14127     /**
14128      * Resizes the dialog to fit the specified content size.
14129      * @param {Number} width
14130      * @param {Number} height
14131      * @return {Roo.BasicDialog} this
14132      */
14133     setContentSize : function(w, h){
14134         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14135         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14136         //if(!this.el.isBorderBox()){
14137             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14138             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14139         //}
14140         if(this.tabs){
14141             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14142             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14143         }
14144         this.resizeTo(w, h);
14145         return this;
14146     },
14147
14148     /**
14149      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14150      * executed in response to a particular key being pressed while the dialog is active.
14151      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14152      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14153      * @param {Function} fn The function to call
14154      * @param {Object} scope (optional) The scope of the function
14155      * @return {Roo.BasicDialog} this
14156      */
14157     addKeyListener : function(key, fn, scope){
14158         var keyCode, shift, ctrl, alt;
14159         if(typeof key == "object" && !(key instanceof Array)){
14160             keyCode = key["key"];
14161             shift = key["shift"];
14162             ctrl = key["ctrl"];
14163             alt = key["alt"];
14164         }else{
14165             keyCode = key;
14166         }
14167         var handler = function(dlg, e){
14168             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14169                 var k = e.getKey();
14170                 if(keyCode instanceof Array){
14171                     for(var i = 0, len = keyCode.length; i < len; i++){
14172                         if(keyCode[i] == k){
14173                           fn.call(scope || window, dlg, k, e);
14174                           return;
14175                         }
14176                     }
14177                 }else{
14178                     if(k == keyCode){
14179                         fn.call(scope || window, dlg, k, e);
14180                     }
14181                 }
14182             }
14183         };
14184         this.on("keydown", handler);
14185         return this;
14186     },
14187
14188     /**
14189      * Returns the TabPanel component (creates it if it doesn't exist).
14190      * Note: If you wish to simply check for the existence of tabs without creating them,
14191      * check for a null 'tabs' property.
14192      * @return {Roo.TabPanel} The tabs component
14193      */
14194     getTabs : function(){
14195         if(!this.tabs){
14196             this.el.addClass("x-dlg-auto-tabs");
14197             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14198             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14199         }
14200         return this.tabs;
14201     },
14202
14203     /**
14204      * Adds a button to the footer section of the dialog.
14205      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14206      * object or a valid Roo.DomHelper element config
14207      * @param {Function} handler The function called when the button is clicked
14208      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14209      * @return {Roo.Button} The new button
14210      */
14211     addButton : function(config, handler, scope){
14212         var dh = Roo.DomHelper;
14213         if(!this.footer){
14214             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14215         }
14216         if(!this.btnContainer){
14217             var tb = this.footer.createChild({
14218
14219                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14220                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14221             }, null, true);
14222             this.btnContainer = tb.firstChild.firstChild.firstChild;
14223         }
14224         var bconfig = {
14225             handler: handler,
14226             scope: scope,
14227             minWidth: this.minButtonWidth,
14228             hideParent:true
14229         };
14230         if(typeof config == "string"){
14231             bconfig.text = config;
14232         }else{
14233             if(config.tag){
14234                 bconfig.dhconfig = config;
14235             }else{
14236                 Roo.apply(bconfig, config);
14237             }
14238         }
14239         var fc = false;
14240         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14241             bconfig.position = Math.max(0, bconfig.position);
14242             fc = this.btnContainer.childNodes[bconfig.position];
14243         }
14244          
14245         var btn = new Roo.Button(
14246             fc ? 
14247                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14248                 : this.btnContainer.appendChild(document.createElement("td")),
14249             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14250             bconfig
14251         );
14252         this.syncBodyHeight();
14253         if(!this.buttons){
14254             /**
14255              * Array of all the buttons that have been added to this dialog via addButton
14256              * @type Array
14257              */
14258             this.buttons = [];
14259         }
14260         this.buttons.push(btn);
14261         return btn;
14262     },
14263
14264     /**
14265      * Sets the default button to be focused when the dialog is displayed.
14266      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14267      * @return {Roo.BasicDialog} this
14268      */
14269     setDefaultButton : function(btn){
14270         this.defaultButton = btn;
14271         return this;
14272     },
14273
14274     // private
14275     getHeaderFooterHeight : function(safe){
14276         var height = 0;
14277         if(this.header){
14278            height += this.header.getHeight();
14279         }
14280         if(this.footer){
14281            var fm = this.footer.getMargins();
14282             height += (this.footer.getHeight()+fm.top+fm.bottom);
14283         }
14284         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14285         height += this.centerBg.getPadding("tb");
14286         return height;
14287     },
14288
14289     // private
14290     syncBodyHeight : function()
14291     {
14292         var bd = this.body, // the text
14293             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14294             bw = this.bwrap;
14295         var height = this.size.height - this.getHeaderFooterHeight(false);
14296         bd.setHeight(height-bd.getMargins("tb"));
14297         var hh = this.header.getHeight();
14298         var h = this.size.height-hh;
14299         cb.setHeight(h);
14300         
14301         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14302         bw.setHeight(h-cb.getPadding("tb"));
14303         
14304         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14305         bd.setWidth(bw.getWidth(true));
14306         if(this.tabs){
14307             this.tabs.syncHeight();
14308             if(Roo.isIE){
14309                 this.tabs.el.repaint();
14310             }
14311         }
14312     },
14313
14314     /**
14315      * Restores the previous state of the dialog if Roo.state is configured.
14316      * @return {Roo.BasicDialog} this
14317      */
14318     restoreState : function(){
14319         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14320         if(box && box.width){
14321             this.xy = [box.x, box.y];
14322             this.resizeTo(box.width, box.height);
14323         }
14324         return this;
14325     },
14326
14327     // private
14328     beforeShow : function(){
14329         this.expand();
14330         if(this.fixedcenter){
14331             this.xy = this.el.getCenterXY(true);
14332         }
14333         if(this.modal){
14334             Roo.get(document.body).addClass("x-body-masked");
14335             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14336             this.mask.show();
14337         }
14338         this.constrainXY();
14339     },
14340
14341     // private
14342     animShow : function(){
14343         var b = Roo.get(this.animateTarget).getBox();
14344         this.proxy.setSize(b.width, b.height);
14345         this.proxy.setLocation(b.x, b.y);
14346         this.proxy.show();
14347         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14348                     true, .35, this.showEl.createDelegate(this));
14349     },
14350
14351     /**
14352      * Shows the dialog.
14353      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14354      * @return {Roo.BasicDialog} this
14355      */
14356     show : function(animateTarget){
14357         if (this.fireEvent("beforeshow", this) === false){
14358             return;
14359         }
14360         if(this.syncHeightBeforeShow){
14361             this.syncBodyHeight();
14362         }else if(this.firstShow){
14363             this.firstShow = false;
14364             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14365         }
14366         this.animateTarget = animateTarget || this.animateTarget;
14367         if(!this.el.isVisible()){
14368             this.beforeShow();
14369             if(this.animateTarget && Roo.get(this.animateTarget)){
14370                 this.animShow();
14371             }else{
14372                 this.showEl();
14373             }
14374         }
14375         return this;
14376     },
14377
14378     // private
14379     showEl : function(){
14380         this.proxy.hide();
14381         this.el.setXY(this.xy);
14382         this.el.show();
14383         this.adjustAssets(true);
14384         this.toFront();
14385         this.focus();
14386         // IE peekaboo bug - fix found by Dave Fenwick
14387         if(Roo.isIE){
14388             this.el.repaint();
14389         }
14390         this.fireEvent("show", this);
14391     },
14392
14393     /**
14394      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14395      * dialog itself will receive focus.
14396      */
14397     focus : function(){
14398         if(this.defaultButton){
14399             this.defaultButton.focus();
14400         }else{
14401             this.focusEl.focus();
14402         }
14403     },
14404
14405     // private
14406     constrainXY : function(){
14407         if(this.constraintoviewport !== false){
14408             if(!this.viewSize){
14409                 if(this.container){
14410                     var s = this.container.getSize();
14411                     this.viewSize = [s.width, s.height];
14412                 }else{
14413                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14414                 }
14415             }
14416             var s = Roo.get(this.container||document).getScroll();
14417
14418             var x = this.xy[0], y = this.xy[1];
14419             var w = this.size.width, h = this.size.height;
14420             var vw = this.viewSize[0], vh = this.viewSize[1];
14421             // only move it if it needs it
14422             var moved = false;
14423             // first validate right/bottom
14424             if(x + w > vw+s.left){
14425                 x = vw - w;
14426                 moved = true;
14427             }
14428             if(y + h > vh+s.top){
14429                 y = vh - h;
14430                 moved = true;
14431             }
14432             // then make sure top/left isn't negative
14433             if(x < s.left){
14434                 x = s.left;
14435                 moved = true;
14436             }
14437             if(y < s.top){
14438                 y = s.top;
14439                 moved = true;
14440             }
14441             if(moved){
14442                 // cache xy
14443                 this.xy = [x, y];
14444                 if(this.isVisible()){
14445                     this.el.setLocation(x, y);
14446                     this.adjustAssets();
14447                 }
14448             }
14449         }
14450     },
14451
14452     // private
14453     onDrag : function(){
14454         if(!this.proxyDrag){
14455             this.xy = this.el.getXY();
14456             this.adjustAssets();
14457         }
14458     },
14459
14460     // private
14461     adjustAssets : function(doShow){
14462         var x = this.xy[0], y = this.xy[1];
14463         var w = this.size.width, h = this.size.height;
14464         if(doShow === true){
14465             if(this.shadow){
14466                 this.shadow.show(this.el);
14467             }
14468             if(this.shim){
14469                 this.shim.show();
14470             }
14471         }
14472         if(this.shadow && this.shadow.isVisible()){
14473             this.shadow.show(this.el);
14474         }
14475         if(this.shim && this.shim.isVisible()){
14476             this.shim.setBounds(x, y, w, h);
14477         }
14478     },
14479
14480     // private
14481     adjustViewport : function(w, h){
14482         if(!w || !h){
14483             w = Roo.lib.Dom.getViewWidth();
14484             h = Roo.lib.Dom.getViewHeight();
14485         }
14486         // cache the size
14487         this.viewSize = [w, h];
14488         if(this.modal && this.mask.isVisible()){
14489             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14490             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14491         }
14492         if(this.isVisible()){
14493             this.constrainXY();
14494         }
14495     },
14496
14497     /**
14498      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14499      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14500      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14501      */
14502     destroy : function(removeEl){
14503         if(this.isVisible()){
14504             this.animateTarget = null;
14505             this.hide();
14506         }
14507         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14508         if(this.tabs){
14509             this.tabs.destroy(removeEl);
14510         }
14511         Roo.destroy(
14512              this.shim,
14513              this.proxy,
14514              this.resizer,
14515              this.close,
14516              this.mask
14517         );
14518         if(this.dd){
14519             this.dd.unreg();
14520         }
14521         if(this.buttons){
14522            for(var i = 0, len = this.buttons.length; i < len; i++){
14523                this.buttons[i].destroy();
14524            }
14525         }
14526         this.el.removeAllListeners();
14527         if(removeEl === true){
14528             this.el.update("");
14529             this.el.remove();
14530         }
14531         Roo.DialogManager.unregister(this);
14532     },
14533
14534     // private
14535     startMove : function(){
14536         if(this.proxyDrag){
14537             this.proxy.show();
14538         }
14539         if(this.constraintoviewport !== false){
14540             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14541         }
14542     },
14543
14544     // private
14545     endMove : function(){
14546         if(!this.proxyDrag){
14547             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14548         }else{
14549             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14550             this.proxy.hide();
14551         }
14552         this.refreshSize();
14553         this.adjustAssets();
14554         this.focus();
14555         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14556     },
14557
14558     /**
14559      * Brings this dialog to the front of any other visible dialogs
14560      * @return {Roo.BasicDialog} this
14561      */
14562     toFront : function(){
14563         Roo.DialogManager.bringToFront(this);
14564         return this;
14565     },
14566
14567     /**
14568      * Sends this dialog to the back (under) of any other visible dialogs
14569      * @return {Roo.BasicDialog} this
14570      */
14571     toBack : function(){
14572         Roo.DialogManager.sendToBack(this);
14573         return this;
14574     },
14575
14576     /**
14577      * Centers this dialog in the viewport
14578      * @return {Roo.BasicDialog} this
14579      */
14580     center : function(){
14581         var xy = this.el.getCenterXY(true);
14582         this.moveTo(xy[0], xy[1]);
14583         return this;
14584     },
14585
14586     /**
14587      * Moves the dialog's top-left corner to the specified point
14588      * @param {Number} x
14589      * @param {Number} y
14590      * @return {Roo.BasicDialog} this
14591      */
14592     moveTo : function(x, y){
14593         this.xy = [x,y];
14594         if(this.isVisible()){
14595             this.el.setXY(this.xy);
14596             this.adjustAssets();
14597         }
14598         return this;
14599     },
14600
14601     /**
14602      * Aligns the dialog to the specified element
14603      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14604      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14605      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14606      * @return {Roo.BasicDialog} this
14607      */
14608     alignTo : function(element, position, offsets){
14609         this.xy = this.el.getAlignToXY(element, position, offsets);
14610         if(this.isVisible()){
14611             this.el.setXY(this.xy);
14612             this.adjustAssets();
14613         }
14614         return this;
14615     },
14616
14617     /**
14618      * Anchors an element to another element and realigns it when the window is resized.
14619      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14620      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14621      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14622      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14623      * is a number, it is used as the buffer delay (defaults to 50ms).
14624      * @return {Roo.BasicDialog} this
14625      */
14626     anchorTo : function(el, alignment, offsets, monitorScroll){
14627         var action = function(){
14628             this.alignTo(el, alignment, offsets);
14629         };
14630         Roo.EventManager.onWindowResize(action, this);
14631         var tm = typeof monitorScroll;
14632         if(tm != 'undefined'){
14633             Roo.EventManager.on(window, 'scroll', action, this,
14634                 {buffer: tm == 'number' ? monitorScroll : 50});
14635         }
14636         action.call(this);
14637         return this;
14638     },
14639
14640     /**
14641      * Returns true if the dialog is visible
14642      * @return {Boolean}
14643      */
14644     isVisible : function(){
14645         return this.el.isVisible();
14646     },
14647
14648     // private
14649     animHide : function(callback){
14650         var b = Roo.get(this.animateTarget).getBox();
14651         this.proxy.show();
14652         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14653         this.el.hide();
14654         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14655                     this.hideEl.createDelegate(this, [callback]));
14656     },
14657
14658     /**
14659      * Hides the dialog.
14660      * @param {Function} callback (optional) Function to call when the dialog is hidden
14661      * @return {Roo.BasicDialog} this
14662      */
14663     hide : function(callback){
14664         if (this.fireEvent("beforehide", this) === false){
14665             return;
14666         }
14667         if(this.shadow){
14668             this.shadow.hide();
14669         }
14670         if(this.shim) {
14671           this.shim.hide();
14672         }
14673         // sometimes animateTarget seems to get set.. causing problems...
14674         // this just double checks..
14675         if(this.animateTarget && Roo.get(this.animateTarget)) {
14676            this.animHide(callback);
14677         }else{
14678             this.el.hide();
14679             this.hideEl(callback);
14680         }
14681         return this;
14682     },
14683
14684     // private
14685     hideEl : function(callback){
14686         this.proxy.hide();
14687         if(this.modal){
14688             this.mask.hide();
14689             Roo.get(document.body).removeClass("x-body-masked");
14690         }
14691         this.fireEvent("hide", this);
14692         if(typeof callback == "function"){
14693             callback();
14694         }
14695     },
14696
14697     // private
14698     hideAction : function(){
14699         this.setLeft("-10000px");
14700         this.setTop("-10000px");
14701         this.setStyle("visibility", "hidden");
14702     },
14703
14704     // private
14705     refreshSize : function(){
14706         this.size = this.el.getSize();
14707         this.xy = this.el.getXY();
14708         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14709     },
14710
14711     // private
14712     // z-index is managed by the DialogManager and may be overwritten at any time
14713     setZIndex : function(index){
14714         if(this.modal){
14715             this.mask.setStyle("z-index", index);
14716         }
14717         if(this.shim){
14718             this.shim.setStyle("z-index", ++index);
14719         }
14720         if(this.shadow){
14721             this.shadow.setZIndex(++index);
14722         }
14723         this.el.setStyle("z-index", ++index);
14724         if(this.proxy){
14725             this.proxy.setStyle("z-index", ++index);
14726         }
14727         if(this.resizer){
14728             this.resizer.proxy.setStyle("z-index", ++index);
14729         }
14730
14731         this.lastZIndex = index;
14732     },
14733
14734     /**
14735      * Returns the element for this dialog
14736      * @return {Roo.Element} The underlying dialog Element
14737      */
14738     getEl : function(){
14739         return this.el;
14740     }
14741 });
14742
14743 /**
14744  * @class Roo.DialogManager
14745  * Provides global access to BasicDialogs that have been created and
14746  * support for z-indexing (layering) multiple open dialogs.
14747  */
14748 Roo.DialogManager = function(){
14749     var list = {};
14750     var accessList = [];
14751     var front = null;
14752
14753     // private
14754     var sortDialogs = function(d1, d2){
14755         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14756     };
14757
14758     // private
14759     var orderDialogs = function(){
14760         accessList.sort(sortDialogs);
14761         var seed = Roo.DialogManager.zseed;
14762         for(var i = 0, len = accessList.length; i < len; i++){
14763             var dlg = accessList[i];
14764             if(dlg){
14765                 dlg.setZIndex(seed + (i*10));
14766             }
14767         }
14768     };
14769
14770     return {
14771         /**
14772          * The starting z-index for BasicDialogs (defaults to 9000)
14773          * @type Number The z-index value
14774          */
14775         zseed : 9000,
14776
14777         // private
14778         register : function(dlg){
14779             list[dlg.id] = dlg;
14780             accessList.push(dlg);
14781         },
14782
14783         // private
14784         unregister : function(dlg){
14785             delete list[dlg.id];
14786             var i=0;
14787             var len=0;
14788             if(!accessList.indexOf){
14789                 for(  i = 0, len = accessList.length; i < len; i++){
14790                     if(accessList[i] == dlg){
14791                         accessList.splice(i, 1);
14792                         return;
14793                     }
14794                 }
14795             }else{
14796                  i = accessList.indexOf(dlg);
14797                 if(i != -1){
14798                     accessList.splice(i, 1);
14799                 }
14800             }
14801         },
14802
14803         /**
14804          * Gets a registered dialog by id
14805          * @param {String/Object} id The id of the dialog or a dialog
14806          * @return {Roo.BasicDialog} this
14807          */
14808         get : function(id){
14809             return typeof id == "object" ? id : list[id];
14810         },
14811
14812         /**
14813          * Brings the specified dialog to the front
14814          * @param {String/Object} dlg The id of the dialog or a dialog
14815          * @return {Roo.BasicDialog} this
14816          */
14817         bringToFront : function(dlg){
14818             dlg = this.get(dlg);
14819             if(dlg != front){
14820                 front = dlg;
14821                 dlg._lastAccess = new Date().getTime();
14822                 orderDialogs();
14823             }
14824             return dlg;
14825         },
14826
14827         /**
14828          * Sends the specified dialog to the back
14829          * @param {String/Object} dlg The id of the dialog or a dialog
14830          * @return {Roo.BasicDialog} this
14831          */
14832         sendToBack : function(dlg){
14833             dlg = this.get(dlg);
14834             dlg._lastAccess = -(new Date().getTime());
14835             orderDialogs();
14836             return dlg;
14837         },
14838
14839         /**
14840          * Hides all dialogs
14841          */
14842         hideAll : function(){
14843             for(var id in list){
14844                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14845                     list[id].hide();
14846                 }
14847             }
14848         }
14849     };
14850 }();
14851
14852 /**
14853  * @class Roo.LayoutDialog
14854  * @extends Roo.BasicDialog
14855  * Dialog which provides adjustments for working with a layout in a Dialog.
14856  * Add your necessary layout config options to the dialog's config.<br>
14857  * Example usage (including a nested layout):
14858  * <pre><code>
14859 if(!dialog){
14860     dialog = new Roo.LayoutDialog("download-dlg", {
14861         modal: true,
14862         width:600,
14863         height:450,
14864         shadow:true,
14865         minWidth:500,
14866         minHeight:350,
14867         autoTabs:true,
14868         proxyDrag:true,
14869         // layout config merges with the dialog config
14870         center:{
14871             tabPosition: "top",
14872             alwaysShowTabs: true
14873         }
14874     });
14875     dialog.addKeyListener(27, dialog.hide, dialog);
14876     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14877     dialog.addButton("Build It!", this.getDownload, this);
14878
14879     // we can even add nested layouts
14880     var innerLayout = new Roo.BorderLayout("dl-inner", {
14881         east: {
14882             initialSize: 200,
14883             autoScroll:true,
14884             split:true
14885         },
14886         center: {
14887             autoScroll:true
14888         }
14889     });
14890     innerLayout.beginUpdate();
14891     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14892     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14893     innerLayout.endUpdate(true);
14894
14895     var layout = dialog.getLayout();
14896     layout.beginUpdate();
14897     layout.add("center", new Roo.ContentPanel("standard-panel",
14898                         {title: "Download the Source", fitToFrame:true}));
14899     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14900                {title: "Build your own roo.js"}));
14901     layout.getRegion("center").showPanel(sp);
14902     layout.endUpdate();
14903 }
14904 </code></pre>
14905     * @constructor
14906     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14907     * @param {Object} config configuration options
14908   */
14909 Roo.LayoutDialog = function(el, cfg){
14910     
14911     var config=  cfg;
14912     if (typeof(cfg) == 'undefined') {
14913         config = Roo.apply({}, el);
14914         // not sure why we use documentElement here.. - it should always be body.
14915         // IE7 borks horribly if we use documentElement.
14916         // webkit also does not like documentElement - it creates a body element...
14917         el = Roo.get( document.body || document.documentElement ).createChild();
14918         //config.autoCreate = true;
14919     }
14920     
14921     
14922     config.autoTabs = false;
14923     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14924     this.body.setStyle({overflow:"hidden", position:"relative"});
14925     this.layout = new Roo.BorderLayout(this.body.dom, config);
14926     this.layout.monitorWindowResize = false;
14927     this.el.addClass("x-dlg-auto-layout");
14928     // fix case when center region overwrites center function
14929     this.center = Roo.BasicDialog.prototype.center;
14930     this.on("show", this.layout.layout, this.layout, true);
14931     if (config.items) {
14932         var xitems = config.items;
14933         delete config.items;
14934         Roo.each(xitems, this.addxtype, this);
14935     }
14936     
14937     
14938 };
14939 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14940     /**
14941      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14942      * @deprecated
14943      */
14944     endUpdate : function(){
14945         this.layout.endUpdate();
14946     },
14947
14948     /**
14949      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14950      *  @deprecated
14951      */
14952     beginUpdate : function(){
14953         this.layout.beginUpdate();
14954     },
14955
14956     /**
14957      * Get the BorderLayout for this dialog
14958      * @return {Roo.BorderLayout}
14959      */
14960     getLayout : function(){
14961         return this.layout;
14962     },
14963
14964     showEl : function(){
14965         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14966         if(Roo.isIE7){
14967             this.layout.layout();
14968         }
14969     },
14970
14971     // private
14972     // Use the syncHeightBeforeShow config option to control this automatically
14973     syncBodyHeight : function(){
14974         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14975         if(this.layout){this.layout.layout();}
14976     },
14977     
14978       /**
14979      * Add an xtype element (actually adds to the layout.)
14980      * @return {Object} xdata xtype object data.
14981      */
14982     
14983     addxtype : function(c) {
14984         return this.layout.addxtype(c);
14985     }
14986 });/*
14987  * Based on:
14988  * Ext JS Library 1.1.1
14989  * Copyright(c) 2006-2007, Ext JS, LLC.
14990  *
14991  * Originally Released Under LGPL - original licence link has changed is not relivant.
14992  *
14993  * Fork - LGPL
14994  * <script type="text/javascript">
14995  */
14996  
14997 /**
14998  * @class Roo.MessageBox
14999  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15000  * Example usage:
15001  *<pre><code>
15002 // Basic alert:
15003 Roo.Msg.alert('Status', 'Changes saved successfully.');
15004
15005 // Prompt for user data:
15006 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15007     if (btn == 'ok'){
15008         // process text value...
15009     }
15010 });
15011
15012 // Show a dialog using config options:
15013 Roo.Msg.show({
15014    title:'Save Changes?',
15015    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15016    buttons: Roo.Msg.YESNOCANCEL,
15017    fn: processResult,
15018    animEl: 'elId'
15019 });
15020 </code></pre>
15021  * @singleton
15022  */
15023 Roo.MessageBox = function(){
15024     var dlg, opt, mask, waitTimer;
15025     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15026     var buttons, activeTextEl, bwidth;
15027
15028     // private
15029     var handleButton = function(button){
15030         dlg.hide();
15031         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15032     };
15033
15034     // private
15035     var handleHide = function(){
15036         if(opt && opt.cls){
15037             dlg.el.removeClass(opt.cls);
15038         }
15039         if(waitTimer){
15040             Roo.TaskMgr.stop(waitTimer);
15041             waitTimer = null;
15042         }
15043     };
15044
15045     // private
15046     var updateButtons = function(b){
15047         var width = 0;
15048         if(!b){
15049             buttons["ok"].hide();
15050             buttons["cancel"].hide();
15051             buttons["yes"].hide();
15052             buttons["no"].hide();
15053             dlg.footer.dom.style.display = 'none';
15054             return width;
15055         }
15056         dlg.footer.dom.style.display = '';
15057         for(var k in buttons){
15058             if(typeof buttons[k] != "function"){
15059                 if(b[k]){
15060                     buttons[k].show();
15061                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15062                     width += buttons[k].el.getWidth()+15;
15063                 }else{
15064                     buttons[k].hide();
15065                 }
15066             }
15067         }
15068         return width;
15069     };
15070
15071     // private
15072     var handleEsc = function(d, k, e){
15073         if(opt && opt.closable !== false){
15074             dlg.hide();
15075         }
15076         if(e){
15077             e.stopEvent();
15078         }
15079     };
15080
15081     return {
15082         /**
15083          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15084          * @return {Roo.BasicDialog} The BasicDialog element
15085          */
15086         getDialog : function(){
15087            if(!dlg){
15088                 dlg = new Roo.BasicDialog("x-msg-box", {
15089                     autoCreate : true,
15090                     shadow: true,
15091                     draggable: true,
15092                     resizable:false,
15093                     constraintoviewport:false,
15094                     fixedcenter:true,
15095                     collapsible : false,
15096                     shim:true,
15097                     modal: true,
15098                     width:400, height:100,
15099                     buttonAlign:"center",
15100                     closeClick : function(){
15101                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15102                             handleButton("no");
15103                         }else{
15104                             handleButton("cancel");
15105                         }
15106                     }
15107                 });
15108                 dlg.on("hide", handleHide);
15109                 mask = dlg.mask;
15110                 dlg.addKeyListener(27, handleEsc);
15111                 buttons = {};
15112                 var bt = this.buttonText;
15113                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15114                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15115                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15116                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15117                 bodyEl = dlg.body.createChild({
15118
15119                     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>'
15120                 });
15121                 msgEl = bodyEl.dom.firstChild;
15122                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15123                 textboxEl.enableDisplayMode();
15124                 textboxEl.addKeyListener([10,13], function(){
15125                     if(dlg.isVisible() && opt && opt.buttons){
15126                         if(opt.buttons.ok){
15127                             handleButton("ok");
15128                         }else if(opt.buttons.yes){
15129                             handleButton("yes");
15130                         }
15131                     }
15132                 });
15133                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15134                 textareaEl.enableDisplayMode();
15135                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15136                 progressEl.enableDisplayMode();
15137                 var pf = progressEl.dom.firstChild;
15138                 if (pf) {
15139                     pp = Roo.get(pf.firstChild);
15140                     pp.setHeight(pf.offsetHeight);
15141                 }
15142                 
15143             }
15144             return dlg;
15145         },
15146
15147         /**
15148          * Updates the message box body text
15149          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15150          * the XHTML-compliant non-breaking space character '&amp;#160;')
15151          * @return {Roo.MessageBox} This message box
15152          */
15153         updateText : function(text){
15154             if(!dlg.isVisible() && !opt.width){
15155                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15156             }
15157             msgEl.innerHTML = text || '&#160;';
15158       
15159             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15160             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15161             var w = Math.max(
15162                     Math.min(opt.width || cw , this.maxWidth), 
15163                     Math.max(opt.minWidth || this.minWidth, bwidth)
15164             );
15165             if(opt.prompt){
15166                 activeTextEl.setWidth(w);
15167             }
15168             if(dlg.isVisible()){
15169                 dlg.fixedcenter = false;
15170             }
15171             // to big, make it scroll. = But as usual stupid IE does not support
15172             // !important..
15173             
15174             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15175                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15176                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15177             } else {
15178                 bodyEl.dom.style.height = '';
15179                 bodyEl.dom.style.overflowY = '';
15180             }
15181             if (cw > w) {
15182                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15183             } else {
15184                 bodyEl.dom.style.overflowX = '';
15185             }
15186             
15187             dlg.setContentSize(w, bodyEl.getHeight());
15188             if(dlg.isVisible()){
15189                 dlg.fixedcenter = true;
15190             }
15191             return this;
15192         },
15193
15194         /**
15195          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15196          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15197          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15198          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15199          * @return {Roo.MessageBox} This message box
15200          */
15201         updateProgress : function(value, text){
15202             if(text){
15203                 this.updateText(text);
15204             }
15205             if (pp) { // weird bug on my firefox - for some reason this is not defined
15206                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15207             }
15208             return this;
15209         },        
15210
15211         /**
15212          * Returns true if the message box is currently displayed
15213          * @return {Boolean} True if the message box is visible, else false
15214          */
15215         isVisible : function(){
15216             return dlg && dlg.isVisible();  
15217         },
15218
15219         /**
15220          * Hides the message box if it is displayed
15221          */
15222         hide : function(){
15223             if(this.isVisible()){
15224                 dlg.hide();
15225             }  
15226         },
15227
15228         /**
15229          * Displays a new message box, or reinitializes an existing message box, based on the config options
15230          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15231          * The following config object properties are supported:
15232          * <pre>
15233 Property    Type             Description
15234 ----------  ---------------  ------------------------------------------------------------------------------------
15235 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15236                                    closes (defaults to undefined)
15237 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15238                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15239 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15240                                    progress and wait dialogs will ignore this property and always hide the
15241                                    close button as they can only be closed programmatically.
15242 cls               String           A custom CSS class to apply to the message box element
15243 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15244                                    displayed (defaults to 75)
15245 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15246                                    function will be btn (the name of the button that was clicked, if applicable,
15247                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15248                                    Progress and wait dialogs will ignore this option since they do not respond to
15249                                    user actions and can only be closed programmatically, so any required function
15250                                    should be called by the same code after it closes the dialog.
15251 icon              String           A CSS class that provides a background image to be used as an icon for
15252                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15253 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15254 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15255 modal             Boolean          False to allow user interaction with the page while the message box is
15256                                    displayed (defaults to true)
15257 msg               String           A string that will replace the existing message box body text (defaults
15258                                    to the XHTML-compliant non-breaking space character '&#160;')
15259 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15260 progress          Boolean          True to display a progress bar (defaults to false)
15261 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15262 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15263 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15264 title             String           The title text
15265 value             String           The string value to set into the active textbox element if displayed
15266 wait              Boolean          True to display a progress bar (defaults to false)
15267 width             Number           The width of the dialog in pixels
15268 </pre>
15269          *
15270          * Example usage:
15271          * <pre><code>
15272 Roo.Msg.show({
15273    title: 'Address',
15274    msg: 'Please enter your address:',
15275    width: 300,
15276    buttons: Roo.MessageBox.OKCANCEL,
15277    multiline: true,
15278    fn: saveAddress,
15279    animEl: 'addAddressBtn'
15280 });
15281 </code></pre>
15282          * @param {Object} config Configuration options
15283          * @return {Roo.MessageBox} This message box
15284          */
15285         show : function(options)
15286         {
15287             
15288             // this causes nightmares if you show one dialog after another
15289             // especially on callbacks..
15290              
15291             if(this.isVisible()){
15292                 
15293                 this.hide();
15294                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15295                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15296                 Roo.log("New Dialog Message:" +  options.msg )
15297                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15298                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15299                 
15300             }
15301             var d = this.getDialog();
15302             opt = options;
15303             d.setTitle(opt.title || "&#160;");
15304             d.close.setDisplayed(opt.closable !== false);
15305             activeTextEl = textboxEl;
15306             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15307             if(opt.prompt){
15308                 if(opt.multiline){
15309                     textboxEl.hide();
15310                     textareaEl.show();
15311                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15312                         opt.multiline : this.defaultTextHeight);
15313                     activeTextEl = textareaEl;
15314                 }else{
15315                     textboxEl.show();
15316                     textareaEl.hide();
15317                 }
15318             }else{
15319                 textboxEl.hide();
15320                 textareaEl.hide();
15321             }
15322             progressEl.setDisplayed(opt.progress === true);
15323             this.updateProgress(0);
15324             activeTextEl.dom.value = opt.value || "";
15325             if(opt.prompt){
15326                 dlg.setDefaultButton(activeTextEl);
15327             }else{
15328                 var bs = opt.buttons;
15329                 var db = null;
15330                 if(bs && bs.ok){
15331                     db = buttons["ok"];
15332                 }else if(bs && bs.yes){
15333                     db = buttons["yes"];
15334                 }
15335                 dlg.setDefaultButton(db);
15336             }
15337             bwidth = updateButtons(opt.buttons);
15338             this.updateText(opt.msg);
15339             if(opt.cls){
15340                 d.el.addClass(opt.cls);
15341             }
15342             d.proxyDrag = opt.proxyDrag === true;
15343             d.modal = opt.modal !== false;
15344             d.mask = opt.modal !== false ? mask : false;
15345             if(!d.isVisible()){
15346                 // force it to the end of the z-index stack so it gets a cursor in FF
15347                 document.body.appendChild(dlg.el.dom);
15348                 d.animateTarget = null;
15349                 d.show(options.animEl);
15350             }
15351             return this;
15352         },
15353
15354         /**
15355          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15356          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15357          * and closing the message box when the process is complete.
15358          * @param {String} title The title bar text
15359          * @param {String} msg The message box body text
15360          * @return {Roo.MessageBox} This message box
15361          */
15362         progress : function(title, msg){
15363             this.show({
15364                 title : title,
15365                 msg : msg,
15366                 buttons: false,
15367                 progress:true,
15368                 closable:false,
15369                 minWidth: this.minProgressWidth,
15370                 modal : true
15371             });
15372             return this;
15373         },
15374
15375         /**
15376          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15377          * If a callback function is passed it will be called after the user clicks the button, and the
15378          * id of the button that was clicked will be passed as the only parameter to the callback
15379          * (could also be the top-right close button).
15380          * @param {String} title The title bar text
15381          * @param {String} msg The message box body text
15382          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15383          * @param {Object} scope (optional) The scope of the callback function
15384          * @return {Roo.MessageBox} This message box
15385          */
15386         alert : function(title, msg, fn, scope){
15387             this.show({
15388                 title : title,
15389                 msg : msg,
15390                 buttons: this.OK,
15391                 fn: fn,
15392                 scope : scope,
15393                 modal : true
15394             });
15395             return this;
15396         },
15397
15398         /**
15399          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15400          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15401          * You are responsible for closing the message box when the process is complete.
15402          * @param {String} msg The message box body text
15403          * @param {String} title (optional) The title bar text
15404          * @return {Roo.MessageBox} This message box
15405          */
15406         wait : function(msg, title){
15407             this.show({
15408                 title : title,
15409                 msg : msg,
15410                 buttons: false,
15411                 closable:false,
15412                 progress:true,
15413                 modal:true,
15414                 width:300,
15415                 wait:true
15416             });
15417             waitTimer = Roo.TaskMgr.start({
15418                 run: function(i){
15419                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15420                 },
15421                 interval: 1000
15422             });
15423             return this;
15424         },
15425
15426         /**
15427          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15428          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15429          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15430          * @param {String} title The title bar text
15431          * @param {String} msg The message box body text
15432          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15433          * @param {Object} scope (optional) The scope of the callback function
15434          * @return {Roo.MessageBox} This message box
15435          */
15436         confirm : function(title, msg, fn, scope){
15437             this.show({
15438                 title : title,
15439                 msg : msg,
15440                 buttons: this.YESNO,
15441                 fn: fn,
15442                 scope : scope,
15443                 modal : true
15444             });
15445             return this;
15446         },
15447
15448         /**
15449          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15450          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15451          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15452          * (could also be the top-right close button) and the text that was entered will be passed as the two
15453          * parameters to the callback.
15454          * @param {String} title The title bar text
15455          * @param {String} msg The message box body text
15456          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15457          * @param {Object} scope (optional) The scope of the callback function
15458          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15459          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15460          * @return {Roo.MessageBox} This message box
15461          */
15462         prompt : function(title, msg, fn, scope, multiline){
15463             this.show({
15464                 title : title,
15465                 msg : msg,
15466                 buttons: this.OKCANCEL,
15467                 fn: fn,
15468                 minWidth:250,
15469                 scope : scope,
15470                 prompt:true,
15471                 multiline: multiline,
15472                 modal : true
15473             });
15474             return this;
15475         },
15476
15477         /**
15478          * Button config that displays a single OK button
15479          * @type Object
15480          */
15481         OK : {ok:true},
15482         /**
15483          * Button config that displays Yes and No buttons
15484          * @type Object
15485          */
15486         YESNO : {yes:true, no:true},
15487         /**
15488          * Button config that displays OK and Cancel buttons
15489          * @type Object
15490          */
15491         OKCANCEL : {ok:true, cancel:true},
15492         /**
15493          * Button config that displays Yes, No and Cancel buttons
15494          * @type Object
15495          */
15496         YESNOCANCEL : {yes:true, no:true, cancel:true},
15497
15498         /**
15499          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15500          * @type Number
15501          */
15502         defaultTextHeight : 75,
15503         /**
15504          * The maximum width in pixels of the message box (defaults to 600)
15505          * @type Number
15506          */
15507         maxWidth : 600,
15508         /**
15509          * The minimum width in pixels of the message box (defaults to 100)
15510          * @type Number
15511          */
15512         minWidth : 100,
15513         /**
15514          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15515          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15516          * @type Number
15517          */
15518         minProgressWidth : 250,
15519         /**
15520          * An object containing the default button text strings that can be overriden for localized language support.
15521          * Supported properties are: ok, cancel, yes and no.
15522          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15523          * @type Object
15524          */
15525         buttonText : {
15526             ok : "OK",
15527             cancel : "Cancel",
15528             yes : "Yes",
15529             no : "No"
15530         }
15531     };
15532 }();
15533
15534 /**
15535  * Shorthand for {@link Roo.MessageBox}
15536  */
15537 Roo.Msg = Roo.MessageBox;/*
15538  * Based on:
15539  * Ext JS Library 1.1.1
15540  * Copyright(c) 2006-2007, Ext JS, LLC.
15541  *
15542  * Originally Released Under LGPL - original licence link has changed is not relivant.
15543  *
15544  * Fork - LGPL
15545  * <script type="text/javascript">
15546  */
15547 /**
15548  * @class Roo.QuickTips
15549  * Provides attractive and customizable tooltips for any element.
15550  * @singleton
15551  */
15552 Roo.QuickTips = function(){
15553     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15554     var ce, bd, xy, dd;
15555     var visible = false, disabled = true, inited = false;
15556     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15557     
15558     var onOver = function(e){
15559         if(disabled){
15560             return;
15561         }
15562         var t = e.getTarget();
15563         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15564             return;
15565         }
15566         if(ce && t == ce.el){
15567             clearTimeout(hideProc);
15568             return;
15569         }
15570         if(t && tagEls[t.id]){
15571             tagEls[t.id].el = t;
15572             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15573             return;
15574         }
15575         var ttp, et = Roo.fly(t);
15576         var ns = cfg.namespace;
15577         if(tm.interceptTitles && t.title){
15578             ttp = t.title;
15579             t.qtip = ttp;
15580             t.removeAttribute("title");
15581             e.preventDefault();
15582         }else{
15583             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15584         }
15585         if(ttp){
15586             showProc = show.defer(tm.showDelay, tm, [{
15587                 el: t, 
15588                 text: ttp, 
15589                 width: et.getAttributeNS(ns, cfg.width),
15590                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15591                 title: et.getAttributeNS(ns, cfg.title),
15592                     cls: et.getAttributeNS(ns, cfg.cls)
15593             }]);
15594         }
15595     };
15596     
15597     var onOut = function(e){
15598         clearTimeout(showProc);
15599         var t = e.getTarget();
15600         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15601             hideProc = setTimeout(hide, tm.hideDelay);
15602         }
15603     };
15604     
15605     var onMove = function(e){
15606         if(disabled){
15607             return;
15608         }
15609         xy = e.getXY();
15610         xy[1] += 18;
15611         if(tm.trackMouse && ce){
15612             el.setXY(xy);
15613         }
15614     };
15615     
15616     var onDown = function(e){
15617         clearTimeout(showProc);
15618         clearTimeout(hideProc);
15619         if(!e.within(el)){
15620             if(tm.hideOnClick){
15621                 hide();
15622                 tm.disable();
15623                 tm.enable.defer(100, tm);
15624             }
15625         }
15626     };
15627     
15628     var getPad = function(){
15629         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15630     };
15631
15632     var show = function(o){
15633         if(disabled){
15634             return;
15635         }
15636         clearTimeout(dismissProc);
15637         ce = o;
15638         if(removeCls){ // in case manually hidden
15639             el.removeClass(removeCls);
15640             removeCls = null;
15641         }
15642         if(ce.cls){
15643             el.addClass(ce.cls);
15644             removeCls = ce.cls;
15645         }
15646         if(ce.title){
15647             tipTitle.update(ce.title);
15648             tipTitle.show();
15649         }else{
15650             tipTitle.update('');
15651             tipTitle.hide();
15652         }
15653         el.dom.style.width  = tm.maxWidth+'px';
15654         //tipBody.dom.style.width = '';
15655         tipBodyText.update(o.text);
15656         var p = getPad(), w = ce.width;
15657         if(!w){
15658             var td = tipBodyText.dom;
15659             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15660             if(aw > tm.maxWidth){
15661                 w = tm.maxWidth;
15662             }else if(aw < tm.minWidth){
15663                 w = tm.minWidth;
15664             }else{
15665                 w = aw;
15666             }
15667         }
15668         //tipBody.setWidth(w);
15669         el.setWidth(parseInt(w, 10) + p);
15670         if(ce.autoHide === false){
15671             close.setDisplayed(true);
15672             if(dd){
15673                 dd.unlock();
15674             }
15675         }else{
15676             close.setDisplayed(false);
15677             if(dd){
15678                 dd.lock();
15679             }
15680         }
15681         if(xy){
15682             el.avoidY = xy[1]-18;
15683             el.setXY(xy);
15684         }
15685         if(tm.animate){
15686             el.setOpacity(.1);
15687             el.setStyle("visibility", "visible");
15688             el.fadeIn({callback: afterShow});
15689         }else{
15690             afterShow();
15691         }
15692     };
15693     
15694     var afterShow = function(){
15695         if(ce){
15696             el.show();
15697             esc.enable();
15698             if(tm.autoDismiss && ce.autoHide !== false){
15699                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15700             }
15701         }
15702     };
15703     
15704     var hide = function(noanim){
15705         clearTimeout(dismissProc);
15706         clearTimeout(hideProc);
15707         ce = null;
15708         if(el.isVisible()){
15709             esc.disable();
15710             if(noanim !== true && tm.animate){
15711                 el.fadeOut({callback: afterHide});
15712             }else{
15713                 afterHide();
15714             } 
15715         }
15716     };
15717     
15718     var afterHide = function(){
15719         el.hide();
15720         if(removeCls){
15721             el.removeClass(removeCls);
15722             removeCls = null;
15723         }
15724     };
15725     
15726     return {
15727         /**
15728         * @cfg {Number} minWidth
15729         * The minimum width of the quick tip (defaults to 40)
15730         */
15731        minWidth : 40,
15732         /**
15733         * @cfg {Number} maxWidth
15734         * The maximum width of the quick tip (defaults to 300)
15735         */
15736        maxWidth : 300,
15737         /**
15738         * @cfg {Boolean} interceptTitles
15739         * True to automatically use the element's DOM title value if available (defaults to false)
15740         */
15741        interceptTitles : false,
15742         /**
15743         * @cfg {Boolean} trackMouse
15744         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15745         */
15746        trackMouse : false,
15747         /**
15748         * @cfg {Boolean} hideOnClick
15749         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15750         */
15751        hideOnClick : true,
15752         /**
15753         * @cfg {Number} showDelay
15754         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15755         */
15756        showDelay : 500,
15757         /**
15758         * @cfg {Number} hideDelay
15759         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15760         */
15761        hideDelay : 200,
15762         /**
15763         * @cfg {Boolean} autoHide
15764         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15765         * Used in conjunction with hideDelay.
15766         */
15767        autoHide : true,
15768         /**
15769         * @cfg {Boolean}
15770         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15771         * (defaults to true).  Used in conjunction with autoDismissDelay.
15772         */
15773        autoDismiss : true,
15774         /**
15775         * @cfg {Number}
15776         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15777         */
15778        autoDismissDelay : 5000,
15779        /**
15780         * @cfg {Boolean} animate
15781         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15782         */
15783        animate : false,
15784
15785        /**
15786         * @cfg {String} title
15787         * Title text to display (defaults to '').  This can be any valid HTML markup.
15788         */
15789         title: '',
15790        /**
15791         * @cfg {String} text
15792         * Body text to display (defaults to '').  This can be any valid HTML markup.
15793         */
15794         text : '',
15795        /**
15796         * @cfg {String} cls
15797         * A CSS class to apply to the base quick tip element (defaults to '').
15798         */
15799         cls : '',
15800        /**
15801         * @cfg {Number} width
15802         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15803         * minWidth or maxWidth.
15804         */
15805         width : null,
15806
15807     /**
15808      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15809      * or display QuickTips in a page.
15810      */
15811        init : function(){
15812           tm = Roo.QuickTips;
15813           cfg = tm.tagConfig;
15814           if(!inited){
15815               if(!Roo.isReady){ // allow calling of init() before onReady
15816                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15817                   return;
15818               }
15819               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15820               el.fxDefaults = {stopFx: true};
15821               // maximum custom styling
15822               //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>');
15823               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>');              
15824               tipTitle = el.child('h3');
15825               tipTitle.enableDisplayMode("block");
15826               tipBody = el.child('div.x-tip-bd');
15827               tipBodyText = el.child('div.x-tip-bd-inner');
15828               //bdLeft = el.child('div.x-tip-bd-left');
15829               //bdRight = el.child('div.x-tip-bd-right');
15830               close = el.child('div.x-tip-close');
15831               close.enableDisplayMode("block");
15832               close.on("click", hide);
15833               var d = Roo.get(document);
15834               d.on("mousedown", onDown);
15835               d.on("mouseover", onOver);
15836               d.on("mouseout", onOut);
15837               d.on("mousemove", onMove);
15838               esc = d.addKeyListener(27, hide);
15839               esc.disable();
15840               if(Roo.dd.DD){
15841                   dd = el.initDD("default", null, {
15842                       onDrag : function(){
15843                           el.sync();  
15844                       }
15845                   });
15846                   dd.setHandleElId(tipTitle.id);
15847                   dd.lock();
15848               }
15849               inited = true;
15850           }
15851           this.enable(); 
15852        },
15853
15854     /**
15855      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15856      * are supported:
15857      * <pre>
15858 Property    Type                   Description
15859 ----------  ---------------------  ------------------------------------------------------------------------
15860 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15861      * </ul>
15862      * @param {Object} config The config object
15863      */
15864        register : function(config){
15865            var cs = config instanceof Array ? config : arguments;
15866            for(var i = 0, len = cs.length; i < len; i++) {
15867                var c = cs[i];
15868                var target = c.target;
15869                if(target){
15870                    if(target instanceof Array){
15871                        for(var j = 0, jlen = target.length; j < jlen; j++){
15872                            tagEls[target[j]] = c;
15873                        }
15874                    }else{
15875                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15876                    }
15877                }
15878            }
15879        },
15880
15881     /**
15882      * Removes this quick tip from its element and destroys it.
15883      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15884      */
15885        unregister : function(el){
15886            delete tagEls[Roo.id(el)];
15887        },
15888
15889     /**
15890      * Enable this quick tip.
15891      */
15892        enable : function(){
15893            if(inited && disabled){
15894                locks.pop();
15895                if(locks.length < 1){
15896                    disabled = false;
15897                }
15898            }
15899        },
15900
15901     /**
15902      * Disable this quick tip.
15903      */
15904        disable : function(){
15905           disabled = true;
15906           clearTimeout(showProc);
15907           clearTimeout(hideProc);
15908           clearTimeout(dismissProc);
15909           if(ce){
15910               hide(true);
15911           }
15912           locks.push(1);
15913        },
15914
15915     /**
15916      * Returns true if the quick tip is enabled, else false.
15917      */
15918        isEnabled : function(){
15919             return !disabled;
15920        },
15921
15922         // private
15923        tagConfig : {
15924            namespace : "ext",
15925            attribute : "qtip",
15926            width : "width",
15927            target : "target",
15928            title : "qtitle",
15929            hide : "hide",
15930            cls : "qclass"
15931        }
15932    };
15933 }();
15934
15935 // backwards compat
15936 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15937  * Based on:
15938  * Ext JS Library 1.1.1
15939  * Copyright(c) 2006-2007, Ext JS, LLC.
15940  *
15941  * Originally Released Under LGPL - original licence link has changed is not relivant.
15942  *
15943  * Fork - LGPL
15944  * <script type="text/javascript">
15945  */
15946  
15947
15948 /**
15949  * @class Roo.tree.TreePanel
15950  * @extends Roo.data.Tree
15951
15952  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15953  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15954  * @cfg {Boolean} enableDD true to enable drag and drop
15955  * @cfg {Boolean} enableDrag true to enable just drag
15956  * @cfg {Boolean} enableDrop true to enable just drop
15957  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15958  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15959  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15960  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15961  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15962  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15963  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15964  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15965  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15966  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15967  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15968  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15969  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15970  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15971  * @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>
15972  * @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>
15973  * 
15974  * @constructor
15975  * @param {String/HTMLElement/Element} el The container element
15976  * @param {Object} config
15977  */
15978 Roo.tree.TreePanel = function(el, config){
15979     var root = false;
15980     var loader = false;
15981     if (config.root) {
15982         root = config.root;
15983         delete config.root;
15984     }
15985     if (config.loader) {
15986         loader = config.loader;
15987         delete config.loader;
15988     }
15989     
15990     Roo.apply(this, config);
15991     Roo.tree.TreePanel.superclass.constructor.call(this);
15992     this.el = Roo.get(el);
15993     this.el.addClass('x-tree');
15994     //console.log(root);
15995     if (root) {
15996         this.setRootNode( Roo.factory(root, Roo.tree));
15997     }
15998     if (loader) {
15999         this.loader = Roo.factory(loader, Roo.tree);
16000     }
16001    /**
16002     * Read-only. The id of the container element becomes this TreePanel's id.
16003     */
16004     this.id = this.el.id;
16005     this.addEvents({
16006         /**
16007         * @event beforeload
16008         * Fires before a node is loaded, return false to cancel
16009         * @param {Node} node The node being loaded
16010         */
16011         "beforeload" : true,
16012         /**
16013         * @event load
16014         * Fires when a node is loaded
16015         * @param {Node} node The node that was loaded
16016         */
16017         "load" : true,
16018         /**
16019         * @event textchange
16020         * Fires when the text for a node is changed
16021         * @param {Node} node The node
16022         * @param {String} text The new text
16023         * @param {String} oldText The old text
16024         */
16025         "textchange" : true,
16026         /**
16027         * @event beforeexpand
16028         * Fires before a node is expanded, return false to cancel.
16029         * @param {Node} node The node
16030         * @param {Boolean} deep
16031         * @param {Boolean} anim
16032         */
16033         "beforeexpand" : true,
16034         /**
16035         * @event beforecollapse
16036         * Fires before a node is collapsed, return false to cancel.
16037         * @param {Node} node The node
16038         * @param {Boolean} deep
16039         * @param {Boolean} anim
16040         */
16041         "beforecollapse" : true,
16042         /**
16043         * @event expand
16044         * Fires when a node is expanded
16045         * @param {Node} node The node
16046         */
16047         "expand" : true,
16048         /**
16049         * @event disabledchange
16050         * Fires when the disabled status of a node changes
16051         * @param {Node} node The node
16052         * @param {Boolean} disabled
16053         */
16054         "disabledchange" : true,
16055         /**
16056         * @event collapse
16057         * Fires when a node is collapsed
16058         * @param {Node} node The node
16059         */
16060         "collapse" : true,
16061         /**
16062         * @event beforeclick
16063         * Fires before click processing on a node. Return false to cancel the default action.
16064         * @param {Node} node The node
16065         * @param {Roo.EventObject} e The event object
16066         */
16067         "beforeclick":true,
16068         /**
16069         * @event checkchange
16070         * Fires when a node with a checkbox's checked property changes
16071         * @param {Node} this This node
16072         * @param {Boolean} checked
16073         */
16074         "checkchange":true,
16075         /**
16076         * @event click
16077         * Fires when a node is clicked
16078         * @param {Node} node The node
16079         * @param {Roo.EventObject} e The event object
16080         */
16081         "click":true,
16082         /**
16083         * @event dblclick
16084         * Fires when a node is double clicked
16085         * @param {Node} node The node
16086         * @param {Roo.EventObject} e The event object
16087         */
16088         "dblclick":true,
16089         /**
16090         * @event contextmenu
16091         * Fires when a node is right clicked
16092         * @param {Node} node The node
16093         * @param {Roo.EventObject} e The event object
16094         */
16095         "contextmenu":true,
16096         /**
16097         * @event beforechildrenrendered
16098         * Fires right before the child nodes for a node are rendered
16099         * @param {Node} node The node
16100         */
16101         "beforechildrenrendered":true,
16102         /**
16103         * @event startdrag
16104         * Fires when a node starts being dragged
16105         * @param {Roo.tree.TreePanel} this
16106         * @param {Roo.tree.TreeNode} node
16107         * @param {event} e The raw browser event
16108         */ 
16109        "startdrag" : true,
16110        /**
16111         * @event enddrag
16112         * Fires when a drag operation is complete
16113         * @param {Roo.tree.TreePanel} this
16114         * @param {Roo.tree.TreeNode} node
16115         * @param {event} e The raw browser event
16116         */
16117        "enddrag" : true,
16118        /**
16119         * @event dragdrop
16120         * Fires when a dragged node is dropped on a valid DD target
16121         * @param {Roo.tree.TreePanel} this
16122         * @param {Roo.tree.TreeNode} node
16123         * @param {DD} dd The dd it was dropped on
16124         * @param {event} e The raw browser event
16125         */
16126        "dragdrop" : true,
16127        /**
16128         * @event beforenodedrop
16129         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16130         * passed to handlers has the following properties:<br />
16131         * <ul style="padding:5px;padding-left:16px;">
16132         * <li>tree - The TreePanel</li>
16133         * <li>target - The node being targeted for the drop</li>
16134         * <li>data - The drag data from the drag source</li>
16135         * <li>point - The point of the drop - append, above or below</li>
16136         * <li>source - The drag source</li>
16137         * <li>rawEvent - Raw mouse event</li>
16138         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16139         * to be inserted by setting them on this object.</li>
16140         * <li>cancel - Set this to true to cancel the drop.</li>
16141         * </ul>
16142         * @param {Object} dropEvent
16143         */
16144        "beforenodedrop" : true,
16145        /**
16146         * @event nodedrop
16147         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16148         * passed to handlers has the following properties:<br />
16149         * <ul style="padding:5px;padding-left:16px;">
16150         * <li>tree - The TreePanel</li>
16151         * <li>target - The node being targeted for the drop</li>
16152         * <li>data - The drag data from the drag source</li>
16153         * <li>point - The point of the drop - append, above or below</li>
16154         * <li>source - The drag source</li>
16155         * <li>rawEvent - Raw mouse event</li>
16156         * <li>dropNode - Dropped node(s).</li>
16157         * </ul>
16158         * @param {Object} dropEvent
16159         */
16160        "nodedrop" : true,
16161         /**
16162         * @event nodedragover
16163         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16164         * passed to handlers has the following properties:<br />
16165         * <ul style="padding:5px;padding-left:16px;">
16166         * <li>tree - The TreePanel</li>
16167         * <li>target - The node being targeted for the drop</li>
16168         * <li>data - The drag data from the drag source</li>
16169         * <li>point - The point of the drop - append, above or below</li>
16170         * <li>source - The drag source</li>
16171         * <li>rawEvent - Raw mouse event</li>
16172         * <li>dropNode - Drop node(s) provided by the source.</li>
16173         * <li>cancel - Set this to true to signal drop not allowed.</li>
16174         * </ul>
16175         * @param {Object} dragOverEvent
16176         */
16177        "nodedragover" : true
16178         
16179     });
16180     if(this.singleExpand){
16181        this.on("beforeexpand", this.restrictExpand, this);
16182     }
16183     if (this.editor) {
16184         this.editor.tree = this;
16185         this.editor = Roo.factory(this.editor, Roo.tree);
16186     }
16187     
16188     if (this.selModel) {
16189         this.selModel = Roo.factory(this.selModel, Roo.tree);
16190     }
16191    
16192 };
16193 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16194     rootVisible : true,
16195     animate: Roo.enableFx,
16196     lines : true,
16197     enableDD : false,
16198     hlDrop : Roo.enableFx,
16199   
16200     renderer: false,
16201     
16202     rendererTip: false,
16203     // private
16204     restrictExpand : function(node){
16205         var p = node.parentNode;
16206         if(p){
16207             if(p.expandedChild && p.expandedChild.parentNode == p){
16208                 p.expandedChild.collapse();
16209             }
16210             p.expandedChild = node;
16211         }
16212     },
16213
16214     // private override
16215     setRootNode : function(node){
16216         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16217         if(!this.rootVisible){
16218             node.ui = new Roo.tree.RootTreeNodeUI(node);
16219         }
16220         return node;
16221     },
16222
16223     /**
16224      * Returns the container element for this TreePanel
16225      */
16226     getEl : function(){
16227         return this.el;
16228     },
16229
16230     /**
16231      * Returns the default TreeLoader for this TreePanel
16232      */
16233     getLoader : function(){
16234         return this.loader;
16235     },
16236
16237     /**
16238      * Expand all nodes
16239      */
16240     expandAll : function(){
16241         this.root.expand(true);
16242     },
16243
16244     /**
16245      * Collapse all nodes
16246      */
16247     collapseAll : function(){
16248         this.root.collapse(true);
16249     },
16250
16251     /**
16252      * Returns the selection model used by this TreePanel
16253      */
16254     getSelectionModel : function(){
16255         if(!this.selModel){
16256             this.selModel = new Roo.tree.DefaultSelectionModel();
16257         }
16258         return this.selModel;
16259     },
16260
16261     /**
16262      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16263      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16264      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16265      * @return {Array}
16266      */
16267     getChecked : function(a, startNode){
16268         startNode = startNode || this.root;
16269         var r = [];
16270         var f = function(){
16271             if(this.attributes.checked){
16272                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16273             }
16274         }
16275         startNode.cascade(f);
16276         return r;
16277     },
16278
16279     /**
16280      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16281      * @param {String} path
16282      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16283      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16284      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16285      */
16286     expandPath : function(path, attr, callback){
16287         attr = attr || "id";
16288         var keys = path.split(this.pathSeparator);
16289         var curNode = this.root;
16290         if(curNode.attributes[attr] != keys[1]){ // invalid root
16291             if(callback){
16292                 callback(false, null);
16293             }
16294             return;
16295         }
16296         var index = 1;
16297         var f = function(){
16298             if(++index == keys.length){
16299                 if(callback){
16300                     callback(true, curNode);
16301                 }
16302                 return;
16303             }
16304             var c = curNode.findChild(attr, keys[index]);
16305             if(!c){
16306                 if(callback){
16307                     callback(false, curNode);
16308                 }
16309                 return;
16310             }
16311             curNode = c;
16312             c.expand(false, false, f);
16313         };
16314         curNode.expand(false, false, f);
16315     },
16316
16317     /**
16318      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16319      * @param {String} path
16320      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16321      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16322      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16323      */
16324     selectPath : function(path, attr, callback){
16325         attr = attr || "id";
16326         var keys = path.split(this.pathSeparator);
16327         var v = keys.pop();
16328         if(keys.length > 0){
16329             var f = function(success, node){
16330                 if(success && node){
16331                     var n = node.findChild(attr, v);
16332                     if(n){
16333                         n.select();
16334                         if(callback){
16335                             callback(true, n);
16336                         }
16337                     }else if(callback){
16338                         callback(false, n);
16339                     }
16340                 }else{
16341                     if(callback){
16342                         callback(false, n);
16343                     }
16344                 }
16345             };
16346             this.expandPath(keys.join(this.pathSeparator), attr, f);
16347         }else{
16348             this.root.select();
16349             if(callback){
16350                 callback(true, this.root);
16351             }
16352         }
16353     },
16354
16355     getTreeEl : function(){
16356         return this.el;
16357     },
16358
16359     /**
16360      * Trigger rendering of this TreePanel
16361      */
16362     render : function(){
16363         if (this.innerCt) {
16364             return this; // stop it rendering more than once!!
16365         }
16366         
16367         this.innerCt = this.el.createChild({tag:"ul",
16368                cls:"x-tree-root-ct " +
16369                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16370
16371         if(this.containerScroll){
16372             Roo.dd.ScrollManager.register(this.el);
16373         }
16374         if((this.enableDD || this.enableDrop) && !this.dropZone){
16375            /**
16376             * The dropZone used by this tree if drop is enabled
16377             * @type Roo.tree.TreeDropZone
16378             */
16379              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16380                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16381            });
16382         }
16383         if((this.enableDD || this.enableDrag) && !this.dragZone){
16384            /**
16385             * The dragZone used by this tree if drag is enabled
16386             * @type Roo.tree.TreeDragZone
16387             */
16388             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16389                ddGroup: this.ddGroup || "TreeDD",
16390                scroll: this.ddScroll
16391            });
16392         }
16393         this.getSelectionModel().init(this);
16394         if (!this.root) {
16395             Roo.log("ROOT not set in tree");
16396             return this;
16397         }
16398         this.root.render();
16399         if(!this.rootVisible){
16400             this.root.renderChildren();
16401         }
16402         return this;
16403     }
16404 });/*
16405  * Based on:
16406  * Ext JS Library 1.1.1
16407  * Copyright(c) 2006-2007, Ext JS, LLC.
16408  *
16409  * Originally Released Under LGPL - original licence link has changed is not relivant.
16410  *
16411  * Fork - LGPL
16412  * <script type="text/javascript">
16413  */
16414  
16415
16416 /**
16417  * @class Roo.tree.DefaultSelectionModel
16418  * @extends Roo.util.Observable
16419  * The default single selection for a TreePanel.
16420  * @param {Object} cfg Configuration
16421  */
16422 Roo.tree.DefaultSelectionModel = function(cfg){
16423    this.selNode = null;
16424    
16425    
16426    
16427    this.addEvents({
16428        /**
16429         * @event selectionchange
16430         * Fires when the selected node changes
16431         * @param {DefaultSelectionModel} this
16432         * @param {TreeNode} node the new selection
16433         */
16434        "selectionchange" : true,
16435
16436        /**
16437         * @event beforeselect
16438         * Fires before the selected node changes, return false to cancel the change
16439         * @param {DefaultSelectionModel} this
16440         * @param {TreeNode} node the new selection
16441         * @param {TreeNode} node the old selection
16442         */
16443        "beforeselect" : true
16444    });
16445    
16446     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16447 };
16448
16449 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16450     init : function(tree){
16451         this.tree = tree;
16452         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16453         tree.on("click", this.onNodeClick, this);
16454     },
16455     
16456     onNodeClick : function(node, e){
16457         if (e.ctrlKey && this.selNode == node)  {
16458             this.unselect(node);
16459             return;
16460         }
16461         this.select(node);
16462     },
16463     
16464     /**
16465      * Select a node.
16466      * @param {TreeNode} node The node to select
16467      * @return {TreeNode} The selected node
16468      */
16469     select : function(node){
16470         var last = this.selNode;
16471         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16472             if(last){
16473                 last.ui.onSelectedChange(false);
16474             }
16475             this.selNode = node;
16476             node.ui.onSelectedChange(true);
16477             this.fireEvent("selectionchange", this, node, last);
16478         }
16479         return node;
16480     },
16481     
16482     /**
16483      * Deselect a node.
16484      * @param {TreeNode} node The node to unselect
16485      */
16486     unselect : function(node){
16487         if(this.selNode == node){
16488             this.clearSelections();
16489         }    
16490     },
16491     
16492     /**
16493      * Clear all selections
16494      */
16495     clearSelections : function(){
16496         var n = this.selNode;
16497         if(n){
16498             n.ui.onSelectedChange(false);
16499             this.selNode = null;
16500             this.fireEvent("selectionchange", this, null);
16501         }
16502         return n;
16503     },
16504     
16505     /**
16506      * Get the selected node
16507      * @return {TreeNode} The selected node
16508      */
16509     getSelectedNode : function(){
16510         return this.selNode;    
16511     },
16512     
16513     /**
16514      * Returns true if the node is selected
16515      * @param {TreeNode} node The node to check
16516      * @return {Boolean}
16517      */
16518     isSelected : function(node){
16519         return this.selNode == node;  
16520     },
16521
16522     /**
16523      * Selects the node above the selected node in the tree, intelligently walking the nodes
16524      * @return TreeNode The new selection
16525      */
16526     selectPrevious : function(){
16527         var s = this.selNode || this.lastSelNode;
16528         if(!s){
16529             return null;
16530         }
16531         var ps = s.previousSibling;
16532         if(ps){
16533             if(!ps.isExpanded() || ps.childNodes.length < 1){
16534                 return this.select(ps);
16535             } else{
16536                 var lc = ps.lastChild;
16537                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16538                     lc = lc.lastChild;
16539                 }
16540                 return this.select(lc);
16541             }
16542         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16543             return this.select(s.parentNode);
16544         }
16545         return null;
16546     },
16547
16548     /**
16549      * Selects the node above the selected node in the tree, intelligently walking the nodes
16550      * @return TreeNode The new selection
16551      */
16552     selectNext : function(){
16553         var s = this.selNode || this.lastSelNode;
16554         if(!s){
16555             return null;
16556         }
16557         if(s.firstChild && s.isExpanded()){
16558              return this.select(s.firstChild);
16559          }else if(s.nextSibling){
16560              return this.select(s.nextSibling);
16561          }else if(s.parentNode){
16562             var newS = null;
16563             s.parentNode.bubble(function(){
16564                 if(this.nextSibling){
16565                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16566                     return false;
16567                 }
16568             });
16569             return newS;
16570          }
16571         return null;
16572     },
16573
16574     onKeyDown : function(e){
16575         var s = this.selNode || this.lastSelNode;
16576         // undesirable, but required
16577         var sm = this;
16578         if(!s){
16579             return;
16580         }
16581         var k = e.getKey();
16582         switch(k){
16583              case e.DOWN:
16584                  e.stopEvent();
16585                  this.selectNext();
16586              break;
16587              case e.UP:
16588                  e.stopEvent();
16589                  this.selectPrevious();
16590              break;
16591              case e.RIGHT:
16592                  e.preventDefault();
16593                  if(s.hasChildNodes()){
16594                      if(!s.isExpanded()){
16595                          s.expand();
16596                      }else if(s.firstChild){
16597                          this.select(s.firstChild, e);
16598                      }
16599                  }
16600              break;
16601              case e.LEFT:
16602                  e.preventDefault();
16603                  if(s.hasChildNodes() && s.isExpanded()){
16604                      s.collapse();
16605                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16606                      this.select(s.parentNode, e);
16607                  }
16608              break;
16609         };
16610     }
16611 });
16612
16613 /**
16614  * @class Roo.tree.MultiSelectionModel
16615  * @extends Roo.util.Observable
16616  * Multi selection for a TreePanel.
16617  * @param {Object} cfg Configuration
16618  */
16619 Roo.tree.MultiSelectionModel = function(){
16620    this.selNodes = [];
16621    this.selMap = {};
16622    this.addEvents({
16623        /**
16624         * @event selectionchange
16625         * Fires when the selected nodes change
16626         * @param {MultiSelectionModel} this
16627         * @param {Array} nodes Array of the selected nodes
16628         */
16629        "selectionchange" : true
16630    });
16631    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16632    
16633 };
16634
16635 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16636     init : function(tree){
16637         this.tree = tree;
16638         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16639         tree.on("click", this.onNodeClick, this);
16640     },
16641     
16642     onNodeClick : function(node, e){
16643         this.select(node, e, e.ctrlKey);
16644     },
16645     
16646     /**
16647      * Select a node.
16648      * @param {TreeNode} node The node to select
16649      * @param {EventObject} e (optional) An event associated with the selection
16650      * @param {Boolean} keepExisting True to retain existing selections
16651      * @return {TreeNode} The selected node
16652      */
16653     select : function(node, e, keepExisting){
16654         if(keepExisting !== true){
16655             this.clearSelections(true);
16656         }
16657         if(this.isSelected(node)){
16658             this.lastSelNode = node;
16659             return node;
16660         }
16661         this.selNodes.push(node);
16662         this.selMap[node.id] = node;
16663         this.lastSelNode = node;
16664         node.ui.onSelectedChange(true);
16665         this.fireEvent("selectionchange", this, this.selNodes);
16666         return node;
16667     },
16668     
16669     /**
16670      * Deselect a node.
16671      * @param {TreeNode} node The node to unselect
16672      */
16673     unselect : function(node){
16674         if(this.selMap[node.id]){
16675             node.ui.onSelectedChange(false);
16676             var sn = this.selNodes;
16677             var index = -1;
16678             if(sn.indexOf){
16679                 index = sn.indexOf(node);
16680             }else{
16681                 for(var i = 0, len = sn.length; i < len; i++){
16682                     if(sn[i] == node){
16683                         index = i;
16684                         break;
16685                     }
16686                 }
16687             }
16688             if(index != -1){
16689                 this.selNodes.splice(index, 1);
16690             }
16691             delete this.selMap[node.id];
16692             this.fireEvent("selectionchange", this, this.selNodes);
16693         }
16694     },
16695     
16696     /**
16697      * Clear all selections
16698      */
16699     clearSelections : function(suppressEvent){
16700         var sn = this.selNodes;
16701         if(sn.length > 0){
16702             for(var i = 0, len = sn.length; i < len; i++){
16703                 sn[i].ui.onSelectedChange(false);
16704             }
16705             this.selNodes = [];
16706             this.selMap = {};
16707             if(suppressEvent !== true){
16708                 this.fireEvent("selectionchange", this, this.selNodes);
16709             }
16710         }
16711     },
16712     
16713     /**
16714      * Returns true if the node is selected
16715      * @param {TreeNode} node The node to check
16716      * @return {Boolean}
16717      */
16718     isSelected : function(node){
16719         return this.selMap[node.id] ? true : false;  
16720     },
16721     
16722     /**
16723      * Returns an array of the selected nodes
16724      * @return {Array}
16725      */
16726     getSelectedNodes : function(){
16727         return this.selNodes;    
16728     },
16729
16730     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16731
16732     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16733
16734     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16735 });/*
16736  * Based on:
16737  * Ext JS Library 1.1.1
16738  * Copyright(c) 2006-2007, Ext JS, LLC.
16739  *
16740  * Originally Released Under LGPL - original licence link has changed is not relivant.
16741  *
16742  * Fork - LGPL
16743  * <script type="text/javascript">
16744  */
16745  
16746 /**
16747  * @class Roo.tree.TreeNode
16748  * @extends Roo.data.Node
16749  * @cfg {String} text The text for this node
16750  * @cfg {Boolean} expanded true to start the node expanded
16751  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16752  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16753  * @cfg {Boolean} disabled true to start the node disabled
16754  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16755  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16756  * @cfg {String} cls A css class to be added to the node
16757  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16758  * @cfg {String} href URL of the link used for the node (defaults to #)
16759  * @cfg {String} hrefTarget target frame for the link
16760  * @cfg {String} qtip An Ext QuickTip for the node
16761  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16762  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16763  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16764  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16765  * (defaults to undefined with no checkbox rendered)
16766  * @constructor
16767  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16768  */
16769 Roo.tree.TreeNode = function(attributes){
16770     attributes = attributes || {};
16771     if(typeof attributes == "string"){
16772         attributes = {text: attributes};
16773     }
16774     this.childrenRendered = false;
16775     this.rendered = false;
16776     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16777     this.expanded = attributes.expanded === true;
16778     this.isTarget = attributes.isTarget !== false;
16779     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16780     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16781
16782     /**
16783      * Read-only. The text for this node. To change it use setText().
16784      * @type String
16785      */
16786     this.text = attributes.text;
16787     /**
16788      * True if this node is disabled.
16789      * @type Boolean
16790      */
16791     this.disabled = attributes.disabled === true;
16792
16793     this.addEvents({
16794         /**
16795         * @event textchange
16796         * Fires when the text for this node is changed
16797         * @param {Node} this This node
16798         * @param {String} text The new text
16799         * @param {String} oldText The old text
16800         */
16801         "textchange" : true,
16802         /**
16803         * @event beforeexpand
16804         * Fires before this node is expanded, return false to cancel.
16805         * @param {Node} this This node
16806         * @param {Boolean} deep
16807         * @param {Boolean} anim
16808         */
16809         "beforeexpand" : true,
16810         /**
16811         * @event beforecollapse
16812         * Fires before this node is collapsed, return false to cancel.
16813         * @param {Node} this This node
16814         * @param {Boolean} deep
16815         * @param {Boolean} anim
16816         */
16817         "beforecollapse" : true,
16818         /**
16819         * @event expand
16820         * Fires when this node is expanded
16821         * @param {Node} this This node
16822         */
16823         "expand" : true,
16824         /**
16825         * @event disabledchange
16826         * Fires when the disabled status of this node changes
16827         * @param {Node} this This node
16828         * @param {Boolean} disabled
16829         */
16830         "disabledchange" : true,
16831         /**
16832         * @event collapse
16833         * Fires when this node is collapsed
16834         * @param {Node} this This node
16835         */
16836         "collapse" : true,
16837         /**
16838         * @event beforeclick
16839         * Fires before click processing. Return false to cancel the default action.
16840         * @param {Node} this This node
16841         * @param {Roo.EventObject} e The event object
16842         */
16843         "beforeclick":true,
16844         /**
16845         * @event checkchange
16846         * Fires when a node with a checkbox's checked property changes
16847         * @param {Node} this This node
16848         * @param {Boolean} checked
16849         */
16850         "checkchange":true,
16851         /**
16852         * @event click
16853         * Fires when this node is clicked
16854         * @param {Node} this This node
16855         * @param {Roo.EventObject} e The event object
16856         */
16857         "click":true,
16858         /**
16859         * @event dblclick
16860         * Fires when this node is double clicked
16861         * @param {Node} this This node
16862         * @param {Roo.EventObject} e The event object
16863         */
16864         "dblclick":true,
16865         /**
16866         * @event contextmenu
16867         * Fires when this node is right clicked
16868         * @param {Node} this This node
16869         * @param {Roo.EventObject} e The event object
16870         */
16871         "contextmenu":true,
16872         /**
16873         * @event beforechildrenrendered
16874         * Fires right before the child nodes for this node are rendered
16875         * @param {Node} this This node
16876         */
16877         "beforechildrenrendered":true
16878     });
16879
16880     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16881
16882     /**
16883      * Read-only. The UI for this node
16884      * @type TreeNodeUI
16885      */
16886     this.ui = new uiClass(this);
16887     
16888     // finally support items[]
16889     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16890         return;
16891     }
16892     
16893     
16894     Roo.each(this.attributes.items, function(c) {
16895         this.appendChild(Roo.factory(c,Roo.Tree));
16896     }, this);
16897     delete this.attributes.items;
16898     
16899     
16900     
16901 };
16902 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16903     preventHScroll: true,
16904     /**
16905      * Returns true if this node is expanded
16906      * @return {Boolean}
16907      */
16908     isExpanded : function(){
16909         return this.expanded;
16910     },
16911
16912     /**
16913      * Returns the UI object for this node
16914      * @return {TreeNodeUI}
16915      */
16916     getUI : function(){
16917         return this.ui;
16918     },
16919
16920     // private override
16921     setFirstChild : function(node){
16922         var of = this.firstChild;
16923         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16924         if(this.childrenRendered && of && node != of){
16925             of.renderIndent(true, true);
16926         }
16927         if(this.rendered){
16928             this.renderIndent(true, true);
16929         }
16930     },
16931
16932     // private override
16933     setLastChild : function(node){
16934         var ol = this.lastChild;
16935         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16936         if(this.childrenRendered && ol && node != ol){
16937             ol.renderIndent(true, true);
16938         }
16939         if(this.rendered){
16940             this.renderIndent(true, true);
16941         }
16942     },
16943
16944     // these methods are overridden to provide lazy rendering support
16945     // private override
16946     appendChild : function()
16947     {
16948         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16949         if(node && this.childrenRendered){
16950             node.render();
16951         }
16952         this.ui.updateExpandIcon();
16953         return node;
16954     },
16955
16956     // private override
16957     removeChild : function(node){
16958         this.ownerTree.getSelectionModel().unselect(node);
16959         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16960         // if it's been rendered remove dom node
16961         if(this.childrenRendered){
16962             node.ui.remove();
16963         }
16964         if(this.childNodes.length < 1){
16965             this.collapse(false, false);
16966         }else{
16967             this.ui.updateExpandIcon();
16968         }
16969         if(!this.firstChild) {
16970             this.childrenRendered = false;
16971         }
16972         return node;
16973     },
16974
16975     // private override
16976     insertBefore : function(node, refNode){
16977         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16978         if(newNode && refNode && this.childrenRendered){
16979             node.render();
16980         }
16981         this.ui.updateExpandIcon();
16982         return newNode;
16983     },
16984
16985     /**
16986      * Sets the text for this node
16987      * @param {String} text
16988      */
16989     setText : function(text){
16990         var oldText = this.text;
16991         this.text = text;
16992         this.attributes.text = text;
16993         if(this.rendered){ // event without subscribing
16994             this.ui.onTextChange(this, text, oldText);
16995         }
16996         this.fireEvent("textchange", this, text, oldText);
16997     },
16998
16999     /**
17000      * Triggers selection of this node
17001      */
17002     select : function(){
17003         this.getOwnerTree().getSelectionModel().select(this);
17004     },
17005
17006     /**
17007      * Triggers deselection of this node
17008      */
17009     unselect : function(){
17010         this.getOwnerTree().getSelectionModel().unselect(this);
17011     },
17012
17013     /**
17014      * Returns true if this node is selected
17015      * @return {Boolean}
17016      */
17017     isSelected : function(){
17018         return this.getOwnerTree().getSelectionModel().isSelected(this);
17019     },
17020
17021     /**
17022      * Expand this node.
17023      * @param {Boolean} deep (optional) True to expand all children as well
17024      * @param {Boolean} anim (optional) false to cancel the default animation
17025      * @param {Function} callback (optional) A callback to be called when
17026      * expanding this node completes (does not wait for deep expand to complete).
17027      * Called with 1 parameter, this node.
17028      */
17029     expand : function(deep, anim, callback){
17030         if(!this.expanded){
17031             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17032                 return;
17033             }
17034             if(!this.childrenRendered){
17035                 this.renderChildren();
17036             }
17037             this.expanded = true;
17038             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17039                 this.ui.animExpand(function(){
17040                     this.fireEvent("expand", this);
17041                     if(typeof callback == "function"){
17042                         callback(this);
17043                     }
17044                     if(deep === true){
17045                         this.expandChildNodes(true);
17046                     }
17047                 }.createDelegate(this));
17048                 return;
17049             }else{
17050                 this.ui.expand();
17051                 this.fireEvent("expand", this);
17052                 if(typeof callback == "function"){
17053                     callback(this);
17054                 }
17055             }
17056         }else{
17057            if(typeof callback == "function"){
17058                callback(this);
17059            }
17060         }
17061         if(deep === true){
17062             this.expandChildNodes(true);
17063         }
17064     },
17065
17066     isHiddenRoot : function(){
17067         return this.isRoot && !this.getOwnerTree().rootVisible;
17068     },
17069
17070     /**
17071      * Collapse this node.
17072      * @param {Boolean} deep (optional) True to collapse all children as well
17073      * @param {Boolean} anim (optional) false to cancel the default animation
17074      */
17075     collapse : function(deep, anim){
17076         if(this.expanded && !this.isHiddenRoot()){
17077             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17078                 return;
17079             }
17080             this.expanded = false;
17081             if((this.getOwnerTree().animate && anim !== false) || anim){
17082                 this.ui.animCollapse(function(){
17083                     this.fireEvent("collapse", this);
17084                     if(deep === true){
17085                         this.collapseChildNodes(true);
17086                     }
17087                 }.createDelegate(this));
17088                 return;
17089             }else{
17090                 this.ui.collapse();
17091                 this.fireEvent("collapse", this);
17092             }
17093         }
17094         if(deep === true){
17095             var cs = this.childNodes;
17096             for(var i = 0, len = cs.length; i < len; i++) {
17097                 cs[i].collapse(true, false);
17098             }
17099         }
17100     },
17101
17102     // private
17103     delayedExpand : function(delay){
17104         if(!this.expandProcId){
17105             this.expandProcId = this.expand.defer(delay, this);
17106         }
17107     },
17108
17109     // private
17110     cancelExpand : function(){
17111         if(this.expandProcId){
17112             clearTimeout(this.expandProcId);
17113         }
17114         this.expandProcId = false;
17115     },
17116
17117     /**
17118      * Toggles expanded/collapsed state of the node
17119      */
17120     toggle : function(){
17121         if(this.expanded){
17122             this.collapse();
17123         }else{
17124             this.expand();
17125         }
17126     },
17127
17128     /**
17129      * Ensures all parent nodes are expanded
17130      */
17131     ensureVisible : function(callback){
17132         var tree = this.getOwnerTree();
17133         tree.expandPath(this.parentNode.getPath(), false, function(){
17134             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17135             Roo.callback(callback);
17136         }.createDelegate(this));
17137     },
17138
17139     /**
17140      * Expand all child nodes
17141      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17142      */
17143     expandChildNodes : function(deep){
17144         var cs = this.childNodes;
17145         for(var i = 0, len = cs.length; i < len; i++) {
17146                 cs[i].expand(deep);
17147         }
17148     },
17149
17150     /**
17151      * Collapse all child nodes
17152      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17153      */
17154     collapseChildNodes : function(deep){
17155         var cs = this.childNodes;
17156         for(var i = 0, len = cs.length; i < len; i++) {
17157                 cs[i].collapse(deep);
17158         }
17159     },
17160
17161     /**
17162      * Disables this node
17163      */
17164     disable : function(){
17165         this.disabled = true;
17166         this.unselect();
17167         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17168             this.ui.onDisableChange(this, true);
17169         }
17170         this.fireEvent("disabledchange", this, true);
17171     },
17172
17173     /**
17174      * Enables this node
17175      */
17176     enable : function(){
17177         this.disabled = false;
17178         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17179             this.ui.onDisableChange(this, false);
17180         }
17181         this.fireEvent("disabledchange", this, false);
17182     },
17183
17184     // private
17185     renderChildren : function(suppressEvent){
17186         if(suppressEvent !== false){
17187             this.fireEvent("beforechildrenrendered", this);
17188         }
17189         var cs = this.childNodes;
17190         for(var i = 0, len = cs.length; i < len; i++){
17191             cs[i].render(true);
17192         }
17193         this.childrenRendered = true;
17194     },
17195
17196     // private
17197     sort : function(fn, scope){
17198         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17199         if(this.childrenRendered){
17200             var cs = this.childNodes;
17201             for(var i = 0, len = cs.length; i < len; i++){
17202                 cs[i].render(true);
17203             }
17204         }
17205     },
17206
17207     // private
17208     render : function(bulkRender){
17209         this.ui.render(bulkRender);
17210         if(!this.rendered){
17211             this.rendered = true;
17212             if(this.expanded){
17213                 this.expanded = false;
17214                 this.expand(false, false);
17215             }
17216         }
17217     },
17218
17219     // private
17220     renderIndent : function(deep, refresh){
17221         if(refresh){
17222             this.ui.childIndent = null;
17223         }
17224         this.ui.renderIndent();
17225         if(deep === true && this.childrenRendered){
17226             var cs = this.childNodes;
17227             for(var i = 0, len = cs.length; i < len; i++){
17228                 cs[i].renderIndent(true, refresh);
17229             }
17230         }
17231     }
17232 });/*
17233  * Based on:
17234  * Ext JS Library 1.1.1
17235  * Copyright(c) 2006-2007, Ext JS, LLC.
17236  *
17237  * Originally Released Under LGPL - original licence link has changed is not relivant.
17238  *
17239  * Fork - LGPL
17240  * <script type="text/javascript">
17241  */
17242  
17243 /**
17244  * @class Roo.tree.AsyncTreeNode
17245  * @extends Roo.tree.TreeNode
17246  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17247  * @constructor
17248  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17249  */
17250  Roo.tree.AsyncTreeNode = function(config){
17251     this.loaded = false;
17252     this.loading = false;
17253     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17254     /**
17255     * @event beforeload
17256     * Fires before this node is loaded, return false to cancel
17257     * @param {Node} this This node
17258     */
17259     this.addEvents({'beforeload':true, 'load': true});
17260     /**
17261     * @event load
17262     * Fires when this node is loaded
17263     * @param {Node} this This node
17264     */
17265     /**
17266      * The loader used by this node (defaults to using the tree's defined loader)
17267      * @type TreeLoader
17268      * @property loader
17269      */
17270 };
17271 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17272     expand : function(deep, anim, callback){
17273         if(this.loading){ // if an async load is already running, waiting til it's done
17274             var timer;
17275             var f = function(){
17276                 if(!this.loading){ // done loading
17277                     clearInterval(timer);
17278                     this.expand(deep, anim, callback);
17279                 }
17280             }.createDelegate(this);
17281             timer = setInterval(f, 200);
17282             return;
17283         }
17284         if(!this.loaded){
17285             if(this.fireEvent("beforeload", this) === false){
17286                 return;
17287             }
17288             this.loading = true;
17289             this.ui.beforeLoad(this);
17290             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17291             if(loader){
17292                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17293                 return;
17294             }
17295         }
17296         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17297     },
17298     
17299     /**
17300      * Returns true if this node is currently loading
17301      * @return {Boolean}
17302      */
17303     isLoading : function(){
17304         return this.loading;  
17305     },
17306     
17307     loadComplete : function(deep, anim, callback){
17308         this.loading = false;
17309         this.loaded = true;
17310         this.ui.afterLoad(this);
17311         this.fireEvent("load", this);
17312         this.expand(deep, anim, callback);
17313     },
17314     
17315     /**
17316      * Returns true if this node has been loaded
17317      * @return {Boolean}
17318      */
17319     isLoaded : function(){
17320         return this.loaded;
17321     },
17322     
17323     hasChildNodes : function(){
17324         if(!this.isLeaf() && !this.loaded){
17325             return true;
17326         }else{
17327             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17328         }
17329     },
17330
17331     /**
17332      * Trigger a reload for this node
17333      * @param {Function} callback
17334      */
17335     reload : function(callback){
17336         this.collapse(false, false);
17337         while(this.firstChild){
17338             this.removeChild(this.firstChild);
17339         }
17340         this.childrenRendered = false;
17341         this.loaded = false;
17342         if(this.isHiddenRoot()){
17343             this.expanded = false;
17344         }
17345         this.expand(false, false, callback);
17346     }
17347 });/*
17348  * Based on:
17349  * Ext JS Library 1.1.1
17350  * Copyright(c) 2006-2007, Ext JS, LLC.
17351  *
17352  * Originally Released Under LGPL - original licence link has changed is not relivant.
17353  *
17354  * Fork - LGPL
17355  * <script type="text/javascript">
17356  */
17357  
17358 /**
17359  * @class Roo.tree.TreeNodeUI
17360  * @constructor
17361  * @param {Object} node The node to render
17362  * The TreeNode UI implementation is separate from the
17363  * tree implementation. Unless you are customizing the tree UI,
17364  * you should never have to use this directly.
17365  */
17366 Roo.tree.TreeNodeUI = function(node){
17367     this.node = node;
17368     this.rendered = false;
17369     this.animating = false;
17370     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17371 };
17372
17373 Roo.tree.TreeNodeUI.prototype = {
17374     removeChild : function(node){
17375         if(this.rendered){
17376             this.ctNode.removeChild(node.ui.getEl());
17377         }
17378     },
17379
17380     beforeLoad : function(){
17381          this.addClass("x-tree-node-loading");
17382     },
17383
17384     afterLoad : function(){
17385          this.removeClass("x-tree-node-loading");
17386     },
17387
17388     onTextChange : function(node, text, oldText){
17389         if(this.rendered){
17390             this.textNode.innerHTML = text;
17391         }
17392     },
17393
17394     onDisableChange : function(node, state){
17395         this.disabled = state;
17396         if(state){
17397             this.addClass("x-tree-node-disabled");
17398         }else{
17399             this.removeClass("x-tree-node-disabled");
17400         }
17401     },
17402
17403     onSelectedChange : function(state){
17404         if(state){
17405             this.focus();
17406             this.addClass("x-tree-selected");
17407         }else{
17408             //this.blur();
17409             this.removeClass("x-tree-selected");
17410         }
17411     },
17412
17413     onMove : function(tree, node, oldParent, newParent, index, refNode){
17414         this.childIndent = null;
17415         if(this.rendered){
17416             var targetNode = newParent.ui.getContainer();
17417             if(!targetNode){//target not rendered
17418                 this.holder = document.createElement("div");
17419                 this.holder.appendChild(this.wrap);
17420                 return;
17421             }
17422             var insertBefore = refNode ? refNode.ui.getEl() : null;
17423             if(insertBefore){
17424                 targetNode.insertBefore(this.wrap, insertBefore);
17425             }else{
17426                 targetNode.appendChild(this.wrap);
17427             }
17428             this.node.renderIndent(true);
17429         }
17430     },
17431
17432     addClass : function(cls){
17433         if(this.elNode){
17434             Roo.fly(this.elNode).addClass(cls);
17435         }
17436     },
17437
17438     removeClass : function(cls){
17439         if(this.elNode){
17440             Roo.fly(this.elNode).removeClass(cls);
17441         }
17442     },
17443
17444     remove : function(){
17445         if(this.rendered){
17446             this.holder = document.createElement("div");
17447             this.holder.appendChild(this.wrap);
17448         }
17449     },
17450
17451     fireEvent : function(){
17452         return this.node.fireEvent.apply(this.node, arguments);
17453     },
17454
17455     initEvents : function(){
17456         this.node.on("move", this.onMove, this);
17457         var E = Roo.EventManager;
17458         var a = this.anchor;
17459
17460         var el = Roo.fly(a, '_treeui');
17461
17462         if(Roo.isOpera){ // opera render bug ignores the CSS
17463             el.setStyle("text-decoration", "none");
17464         }
17465
17466         el.on("click", this.onClick, this);
17467         el.on("dblclick", this.onDblClick, this);
17468
17469         if(this.checkbox){
17470             Roo.EventManager.on(this.checkbox,
17471                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17472         }
17473
17474         el.on("contextmenu", this.onContextMenu, this);
17475
17476         var icon = Roo.fly(this.iconNode);
17477         icon.on("click", this.onClick, this);
17478         icon.on("dblclick", this.onDblClick, this);
17479         icon.on("contextmenu", this.onContextMenu, this);
17480         E.on(this.ecNode, "click", this.ecClick, this, true);
17481
17482         if(this.node.disabled){
17483             this.addClass("x-tree-node-disabled");
17484         }
17485         if(this.node.hidden){
17486             this.addClass("x-tree-node-disabled");
17487         }
17488         var ot = this.node.getOwnerTree();
17489         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17490         if(dd && (!this.node.isRoot || ot.rootVisible)){
17491             Roo.dd.Registry.register(this.elNode, {
17492                 node: this.node,
17493                 handles: this.getDDHandles(),
17494                 isHandle: false
17495             });
17496         }
17497     },
17498
17499     getDDHandles : function(){
17500         return [this.iconNode, this.textNode];
17501     },
17502
17503     hide : function(){
17504         if(this.rendered){
17505             this.wrap.style.display = "none";
17506         }
17507     },
17508
17509     show : function(){
17510         if(this.rendered){
17511             this.wrap.style.display = "";
17512         }
17513     },
17514
17515     onContextMenu : function(e){
17516         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17517             e.preventDefault();
17518             this.focus();
17519             this.fireEvent("contextmenu", this.node, e);
17520         }
17521     },
17522
17523     onClick : function(e){
17524         if(this.dropping){
17525             e.stopEvent();
17526             return;
17527         }
17528         if(this.fireEvent("beforeclick", this.node, e) !== false){
17529             if(!this.disabled && this.node.attributes.href){
17530                 this.fireEvent("click", this.node, e);
17531                 return;
17532             }
17533             e.preventDefault();
17534             if(this.disabled){
17535                 return;
17536             }
17537
17538             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17539                 this.node.toggle();
17540             }
17541
17542             this.fireEvent("click", this.node, e);
17543         }else{
17544             e.stopEvent();
17545         }
17546     },
17547
17548     onDblClick : function(e){
17549         e.preventDefault();
17550         if(this.disabled){
17551             return;
17552         }
17553         if(this.checkbox){
17554             this.toggleCheck();
17555         }
17556         if(!this.animating && this.node.hasChildNodes()){
17557             this.node.toggle();
17558         }
17559         this.fireEvent("dblclick", this.node, e);
17560     },
17561
17562     onCheckChange : function(){
17563         var checked = this.checkbox.checked;
17564         this.node.attributes.checked = checked;
17565         this.fireEvent('checkchange', this.node, checked);
17566     },
17567
17568     ecClick : function(e){
17569         if(!this.animating && this.node.hasChildNodes()){
17570             this.node.toggle();
17571         }
17572     },
17573
17574     startDrop : function(){
17575         this.dropping = true;
17576     },
17577
17578     // delayed drop so the click event doesn't get fired on a drop
17579     endDrop : function(){
17580        setTimeout(function(){
17581            this.dropping = false;
17582        }.createDelegate(this), 50);
17583     },
17584
17585     expand : function(){
17586         this.updateExpandIcon();
17587         this.ctNode.style.display = "";
17588     },
17589
17590     focus : function(){
17591         if(!this.node.preventHScroll){
17592             try{this.anchor.focus();
17593             }catch(e){}
17594         }else if(!Roo.isIE){
17595             try{
17596                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17597                 var l = noscroll.scrollLeft;
17598                 this.anchor.focus();
17599                 noscroll.scrollLeft = l;
17600             }catch(e){}
17601         }
17602     },
17603
17604     toggleCheck : function(value){
17605         var cb = this.checkbox;
17606         if(cb){
17607             cb.checked = (value === undefined ? !cb.checked : value);
17608         }
17609     },
17610
17611     blur : function(){
17612         try{
17613             this.anchor.blur();
17614         }catch(e){}
17615     },
17616
17617     animExpand : function(callback){
17618         var ct = Roo.get(this.ctNode);
17619         ct.stopFx();
17620         if(!this.node.hasChildNodes()){
17621             this.updateExpandIcon();
17622             this.ctNode.style.display = "";
17623             Roo.callback(callback);
17624             return;
17625         }
17626         this.animating = true;
17627         this.updateExpandIcon();
17628
17629         ct.slideIn('t', {
17630            callback : function(){
17631                this.animating = false;
17632                Roo.callback(callback);
17633             },
17634             scope: this,
17635             duration: this.node.ownerTree.duration || .25
17636         });
17637     },
17638
17639     highlight : function(){
17640         var tree = this.node.getOwnerTree();
17641         Roo.fly(this.wrap).highlight(
17642             tree.hlColor || "C3DAF9",
17643             {endColor: tree.hlBaseColor}
17644         );
17645     },
17646
17647     collapse : function(){
17648         this.updateExpandIcon();
17649         this.ctNode.style.display = "none";
17650     },
17651
17652     animCollapse : function(callback){
17653         var ct = Roo.get(this.ctNode);
17654         ct.enableDisplayMode('block');
17655         ct.stopFx();
17656
17657         this.animating = true;
17658         this.updateExpandIcon();
17659
17660         ct.slideOut('t', {
17661             callback : function(){
17662                this.animating = false;
17663                Roo.callback(callback);
17664             },
17665             scope: this,
17666             duration: this.node.ownerTree.duration || .25
17667         });
17668     },
17669
17670     getContainer : function(){
17671         return this.ctNode;
17672     },
17673
17674     getEl : function(){
17675         return this.wrap;
17676     },
17677
17678     appendDDGhost : function(ghostNode){
17679         ghostNode.appendChild(this.elNode.cloneNode(true));
17680     },
17681
17682     getDDRepairXY : function(){
17683         return Roo.lib.Dom.getXY(this.iconNode);
17684     },
17685
17686     onRender : function(){
17687         this.render();
17688     },
17689
17690     render : function(bulkRender){
17691         var n = this.node, a = n.attributes;
17692         var targetNode = n.parentNode ?
17693               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17694
17695         if(!this.rendered){
17696             this.rendered = true;
17697
17698             this.renderElements(n, a, targetNode, bulkRender);
17699
17700             if(a.qtip){
17701                if(this.textNode.setAttributeNS){
17702                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17703                    if(a.qtipTitle){
17704                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17705                    }
17706                }else{
17707                    this.textNode.setAttribute("ext:qtip", a.qtip);
17708                    if(a.qtipTitle){
17709                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17710                    }
17711                }
17712             }else if(a.qtipCfg){
17713                 a.qtipCfg.target = Roo.id(this.textNode);
17714                 Roo.QuickTips.register(a.qtipCfg);
17715             }
17716             this.initEvents();
17717             if(!this.node.expanded){
17718                 this.updateExpandIcon();
17719             }
17720         }else{
17721             if(bulkRender === true) {
17722                 targetNode.appendChild(this.wrap);
17723             }
17724         }
17725     },
17726
17727     renderElements : function(n, a, targetNode, bulkRender)
17728     {
17729         // add some indent caching, this helps performance when rendering a large tree
17730         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17731         var t = n.getOwnerTree();
17732         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17733         if (typeof(n.attributes.html) != 'undefined') {
17734             txt = n.attributes.html;
17735         }
17736         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17737         var cb = typeof a.checked == 'boolean';
17738         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17739         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17740             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17741             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17742             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17743             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17744             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17745              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17746                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17747             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17748             "</li>"];
17749
17750         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17751             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17752                                 n.nextSibling.ui.getEl(), buf.join(""));
17753         }else{
17754             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17755         }
17756
17757         this.elNode = this.wrap.childNodes[0];
17758         this.ctNode = this.wrap.childNodes[1];
17759         var cs = this.elNode.childNodes;
17760         this.indentNode = cs[0];
17761         this.ecNode = cs[1];
17762         this.iconNode = cs[2];
17763         var index = 3;
17764         if(cb){
17765             this.checkbox = cs[3];
17766             index++;
17767         }
17768         this.anchor = cs[index];
17769         this.textNode = cs[index].firstChild;
17770     },
17771
17772     getAnchor : function(){
17773         return this.anchor;
17774     },
17775
17776     getTextEl : function(){
17777         return this.textNode;
17778     },
17779
17780     getIconEl : function(){
17781         return this.iconNode;
17782     },
17783
17784     isChecked : function(){
17785         return this.checkbox ? this.checkbox.checked : false;
17786     },
17787
17788     updateExpandIcon : function(){
17789         if(this.rendered){
17790             var n = this.node, c1, c2;
17791             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17792             var hasChild = n.hasChildNodes();
17793             if(hasChild){
17794                 if(n.expanded){
17795                     cls += "-minus";
17796                     c1 = "x-tree-node-collapsed";
17797                     c2 = "x-tree-node-expanded";
17798                 }else{
17799                     cls += "-plus";
17800                     c1 = "x-tree-node-expanded";
17801                     c2 = "x-tree-node-collapsed";
17802                 }
17803                 if(this.wasLeaf){
17804                     this.removeClass("x-tree-node-leaf");
17805                     this.wasLeaf = false;
17806                 }
17807                 if(this.c1 != c1 || this.c2 != c2){
17808                     Roo.fly(this.elNode).replaceClass(c1, c2);
17809                     this.c1 = c1; this.c2 = c2;
17810                 }
17811             }else{
17812                 // this changes non-leafs into leafs if they have no children.
17813                 // it's not very rational behaviour..
17814                 
17815                 if(!this.wasLeaf && this.node.leaf){
17816                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17817                     delete this.c1;
17818                     delete this.c2;
17819                     this.wasLeaf = true;
17820                 }
17821             }
17822             var ecc = "x-tree-ec-icon "+cls;
17823             if(this.ecc != ecc){
17824                 this.ecNode.className = ecc;
17825                 this.ecc = ecc;
17826             }
17827         }
17828     },
17829
17830     getChildIndent : function(){
17831         if(!this.childIndent){
17832             var buf = [];
17833             var p = this.node;
17834             while(p){
17835                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17836                     if(!p.isLast()) {
17837                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17838                     } else {
17839                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17840                     }
17841                 }
17842                 p = p.parentNode;
17843             }
17844             this.childIndent = buf.join("");
17845         }
17846         return this.childIndent;
17847     },
17848
17849     renderIndent : function(){
17850         if(this.rendered){
17851             var indent = "";
17852             var p = this.node.parentNode;
17853             if(p){
17854                 indent = p.ui.getChildIndent();
17855             }
17856             if(this.indentMarkup != indent){ // don't rerender if not required
17857                 this.indentNode.innerHTML = indent;
17858                 this.indentMarkup = indent;
17859             }
17860             this.updateExpandIcon();
17861         }
17862     }
17863 };
17864
17865 Roo.tree.RootTreeNodeUI = function(){
17866     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17867 };
17868 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17869     render : function(){
17870         if(!this.rendered){
17871             var targetNode = this.node.ownerTree.innerCt.dom;
17872             this.node.expanded = true;
17873             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17874             this.wrap = this.ctNode = targetNode.firstChild;
17875         }
17876     },
17877     collapse : function(){
17878     },
17879     expand : function(){
17880     }
17881 });/*
17882  * Based on:
17883  * Ext JS Library 1.1.1
17884  * Copyright(c) 2006-2007, Ext JS, LLC.
17885  *
17886  * Originally Released Under LGPL - original licence link has changed is not relivant.
17887  *
17888  * Fork - LGPL
17889  * <script type="text/javascript">
17890  */
17891 /**
17892  * @class Roo.tree.TreeLoader
17893  * @extends Roo.util.Observable
17894  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17895  * nodes from a specified URL. The response must be a javascript Array definition
17896  * who's elements are node definition objects. eg:
17897  * <pre><code>
17898 {  success : true,
17899    data :      [
17900    
17901     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17902     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17903     ]
17904 }
17905
17906
17907 </code></pre>
17908  * <br><br>
17909  * The old style respose with just an array is still supported, but not recommended.
17910  * <br><br>
17911  *
17912  * A server request is sent, and child nodes are loaded only when a node is expanded.
17913  * The loading node's id is passed to the server under the parameter name "node" to
17914  * enable the server to produce the correct child nodes.
17915  * <br><br>
17916  * To pass extra parameters, an event handler may be attached to the "beforeload"
17917  * event, and the parameters specified in the TreeLoader's baseParams property:
17918  * <pre><code>
17919     myTreeLoader.on("beforeload", function(treeLoader, node) {
17920         this.baseParams.category = node.attributes.category;
17921     }, this);
17922 </code></pre><
17923  * This would pass an HTTP parameter called "category" to the server containing
17924  * the value of the Node's "category" attribute.
17925  * @constructor
17926  * Creates a new Treeloader.
17927  * @param {Object} config A config object containing config properties.
17928  */
17929 Roo.tree.TreeLoader = function(config){
17930     this.baseParams = {};
17931     this.requestMethod = "POST";
17932     Roo.apply(this, config);
17933
17934     this.addEvents({
17935     
17936         /**
17937          * @event beforeload
17938          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17939          * @param {Object} This TreeLoader object.
17940          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17941          * @param {Object} callback The callback function specified in the {@link #load} call.
17942          */
17943         beforeload : true,
17944         /**
17945          * @event load
17946          * Fires when the node has been successfuly loaded.
17947          * @param {Object} This TreeLoader object.
17948          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17949          * @param {Object} response The response object containing the data from the server.
17950          */
17951         load : true,
17952         /**
17953          * @event loadexception
17954          * Fires if the network request failed.
17955          * @param {Object} This TreeLoader object.
17956          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17957          * @param {Object} response The response object containing the data from the server.
17958          */
17959         loadexception : true,
17960         /**
17961          * @event create
17962          * Fires before a node is created, enabling you to return custom Node types 
17963          * @param {Object} This TreeLoader object.
17964          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17965          */
17966         create : true
17967     });
17968
17969     Roo.tree.TreeLoader.superclass.constructor.call(this);
17970 };
17971
17972 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17973     /**
17974     * @cfg {String} dataUrl The URL from which to request a Json string which
17975     * specifies an array of node definition object representing the child nodes
17976     * to be loaded.
17977     */
17978     /**
17979     * @cfg {String} requestMethod either GET or POST
17980     * defaults to POST (due to BC)
17981     * to be loaded.
17982     */
17983     /**
17984     * @cfg {Object} baseParams (optional) An object containing properties which
17985     * specify HTTP parameters to be passed to each request for child nodes.
17986     */
17987     /**
17988     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17989     * created by this loader. If the attributes sent by the server have an attribute in this object,
17990     * they take priority.
17991     */
17992     /**
17993     * @cfg {Object} uiProviders (optional) An object containing properties which
17994     * 
17995     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17996     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17997     * <i>uiProvider</i> attribute of a returned child node is a string rather
17998     * than a reference to a TreeNodeUI implementation, this that string value
17999     * is used as a property name in the uiProviders object. You can define the provider named
18000     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18001     */
18002     uiProviders : {},
18003
18004     /**
18005     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18006     * child nodes before loading.
18007     */
18008     clearOnLoad : true,
18009
18010     /**
18011     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18012     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18013     * Grid query { data : [ .....] }
18014     */
18015     
18016     root : false,
18017      /**
18018     * @cfg {String} queryParam (optional) 
18019     * Name of the query as it will be passed on the querystring (defaults to 'node')
18020     * eg. the request will be ?node=[id]
18021     */
18022     
18023     
18024     queryParam: false,
18025     
18026     /**
18027      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18028      * This is called automatically when a node is expanded, but may be used to reload
18029      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18030      * @param {Roo.tree.TreeNode} node
18031      * @param {Function} callback
18032      */
18033     load : function(node, callback){
18034         if(this.clearOnLoad){
18035             while(node.firstChild){
18036                 node.removeChild(node.firstChild);
18037             }
18038         }
18039         if(node.attributes.children){ // preloaded json children
18040             var cs = node.attributes.children;
18041             for(var i = 0, len = cs.length; i < len; i++){
18042                 node.appendChild(this.createNode(cs[i]));
18043             }
18044             if(typeof callback == "function"){
18045                 callback();
18046             }
18047         }else if(this.dataUrl){
18048             this.requestData(node, callback);
18049         }
18050     },
18051
18052     getParams: function(node){
18053         var buf = [], bp = this.baseParams;
18054         for(var key in bp){
18055             if(typeof bp[key] != "function"){
18056                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18057             }
18058         }
18059         var n = this.queryParam === false ? 'node' : this.queryParam;
18060         buf.push(n + "=", encodeURIComponent(node.id));
18061         return buf.join("");
18062     },
18063
18064     requestData : function(node, callback){
18065         if(this.fireEvent("beforeload", this, node, callback) !== false){
18066             this.transId = Roo.Ajax.request({
18067                 method:this.requestMethod,
18068                 url: this.dataUrl||this.url,
18069                 success: this.handleResponse,
18070                 failure: this.handleFailure,
18071                 scope: this,
18072                 argument: {callback: callback, node: node},
18073                 params: this.getParams(node)
18074             });
18075         }else{
18076             // if the load is cancelled, make sure we notify
18077             // the node that we are done
18078             if(typeof callback == "function"){
18079                 callback();
18080             }
18081         }
18082     },
18083
18084     isLoading : function(){
18085         return this.transId ? true : false;
18086     },
18087
18088     abort : function(){
18089         if(this.isLoading()){
18090             Roo.Ajax.abort(this.transId);
18091         }
18092     },
18093
18094     // private
18095     createNode : function(attr)
18096     {
18097         // apply baseAttrs, nice idea Corey!
18098         if(this.baseAttrs){
18099             Roo.applyIf(attr, this.baseAttrs);
18100         }
18101         if(this.applyLoader !== false){
18102             attr.loader = this;
18103         }
18104         // uiProvider = depreciated..
18105         
18106         if(typeof(attr.uiProvider) == 'string'){
18107            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18108                 /**  eval:var:attr */ eval(attr.uiProvider);
18109         }
18110         if(typeof(this.uiProviders['default']) != 'undefined') {
18111             attr.uiProvider = this.uiProviders['default'];
18112         }
18113         
18114         this.fireEvent('create', this, attr);
18115         
18116         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18117         return(attr.leaf ?
18118                         new Roo.tree.TreeNode(attr) :
18119                         new Roo.tree.AsyncTreeNode(attr));
18120     },
18121
18122     processResponse : function(response, node, callback)
18123     {
18124         var json = response.responseText;
18125         try {
18126             
18127             var o = Roo.decode(json);
18128             
18129             if (this.root === false && typeof(o.success) != undefined) {
18130                 this.root = 'data'; // the default behaviour for list like data..
18131                 }
18132                 
18133             if (this.root !== false &&  !o.success) {
18134                 // it's a failure condition.
18135                 var a = response.argument;
18136                 this.fireEvent("loadexception", this, a.node, response);
18137                 Roo.log("Load failed - should have a handler really");
18138                 return;
18139             }
18140             
18141             
18142             
18143             if (this.root !== false) {
18144                  o = o[this.root];
18145             }
18146             
18147             for(var i = 0, len = o.length; i < len; i++){
18148                 var n = this.createNode(o[i]);
18149                 if(n){
18150                     node.appendChild(n);
18151                 }
18152             }
18153             if(typeof callback == "function"){
18154                 callback(this, node);
18155             }
18156         }catch(e){
18157             this.handleFailure(response);
18158         }
18159     },
18160
18161     handleResponse : function(response){
18162         this.transId = false;
18163         var a = response.argument;
18164         this.processResponse(response, a.node, a.callback);
18165         this.fireEvent("load", this, a.node, response);
18166     },
18167
18168     handleFailure : function(response)
18169     {
18170         // should handle failure better..
18171         this.transId = false;
18172         var a = response.argument;
18173         this.fireEvent("loadexception", this, a.node, response);
18174         if(typeof a.callback == "function"){
18175             a.callback(this, a.node);
18176         }
18177     }
18178 });/*
18179  * Based on:
18180  * Ext JS Library 1.1.1
18181  * Copyright(c) 2006-2007, Ext JS, LLC.
18182  *
18183  * Originally Released Under LGPL - original licence link has changed is not relivant.
18184  *
18185  * Fork - LGPL
18186  * <script type="text/javascript">
18187  */
18188
18189 /**
18190 * @class Roo.tree.TreeFilter
18191 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18192 * @param {TreePanel} tree
18193 * @param {Object} config (optional)
18194  */
18195 Roo.tree.TreeFilter = function(tree, config){
18196     this.tree = tree;
18197     this.filtered = {};
18198     Roo.apply(this, config);
18199 };
18200
18201 Roo.tree.TreeFilter.prototype = {
18202     clearBlank:false,
18203     reverse:false,
18204     autoClear:false,
18205     remove:false,
18206
18207      /**
18208      * Filter the data by a specific attribute.
18209      * @param {String/RegExp} value Either string that the attribute value
18210      * should start with or a RegExp to test against the attribute
18211      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18212      * @param {TreeNode} startNode (optional) The node to start the filter at.
18213      */
18214     filter : function(value, attr, startNode){
18215         attr = attr || "text";
18216         var f;
18217         if(typeof value == "string"){
18218             var vlen = value.length;
18219             // auto clear empty filter
18220             if(vlen == 0 && this.clearBlank){
18221                 this.clear();
18222                 return;
18223             }
18224             value = value.toLowerCase();
18225             f = function(n){
18226                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18227             };
18228         }else if(value.exec){ // regex?
18229             f = function(n){
18230                 return value.test(n.attributes[attr]);
18231             };
18232         }else{
18233             throw 'Illegal filter type, must be string or regex';
18234         }
18235         this.filterBy(f, null, startNode);
18236         },
18237
18238     /**
18239      * Filter by a function. The passed function will be called with each
18240      * node in the tree (or from the startNode). If the function returns true, the node is kept
18241      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18242      * @param {Function} fn The filter function
18243      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18244      */
18245     filterBy : function(fn, scope, startNode){
18246         startNode = startNode || this.tree.root;
18247         if(this.autoClear){
18248             this.clear();
18249         }
18250         var af = this.filtered, rv = this.reverse;
18251         var f = function(n){
18252             if(n == startNode){
18253                 return true;
18254             }
18255             if(af[n.id]){
18256                 return false;
18257             }
18258             var m = fn.call(scope || n, n);
18259             if(!m || rv){
18260                 af[n.id] = n;
18261                 n.ui.hide();
18262                 return false;
18263             }
18264             return true;
18265         };
18266         startNode.cascade(f);
18267         if(this.remove){
18268            for(var id in af){
18269                if(typeof id != "function"){
18270                    var n = af[id];
18271                    if(n && n.parentNode){
18272                        n.parentNode.removeChild(n);
18273                    }
18274                }
18275            }
18276         }
18277     },
18278
18279     /**
18280      * Clears the current filter. Note: with the "remove" option
18281      * set a filter cannot be cleared.
18282      */
18283     clear : function(){
18284         var t = this.tree;
18285         var af = this.filtered;
18286         for(var id in af){
18287             if(typeof id != "function"){
18288                 var n = af[id];
18289                 if(n){
18290                     n.ui.show();
18291                 }
18292             }
18293         }
18294         this.filtered = {};
18295     }
18296 };
18297 /*
18298  * Based on:
18299  * Ext JS Library 1.1.1
18300  * Copyright(c) 2006-2007, Ext JS, LLC.
18301  *
18302  * Originally Released Under LGPL - original licence link has changed is not relivant.
18303  *
18304  * Fork - LGPL
18305  * <script type="text/javascript">
18306  */
18307  
18308
18309 /**
18310  * @class Roo.tree.TreeSorter
18311  * Provides sorting of nodes in a TreePanel
18312  * 
18313  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18314  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18315  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18316  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18317  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18318  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18319  * @constructor
18320  * @param {TreePanel} tree
18321  * @param {Object} config
18322  */
18323 Roo.tree.TreeSorter = function(tree, config){
18324     Roo.apply(this, config);
18325     tree.on("beforechildrenrendered", this.doSort, this);
18326     tree.on("append", this.updateSort, this);
18327     tree.on("insert", this.updateSort, this);
18328     
18329     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18330     var p = this.property || "text";
18331     var sortType = this.sortType;
18332     var fs = this.folderSort;
18333     var cs = this.caseSensitive === true;
18334     var leafAttr = this.leafAttr || 'leaf';
18335
18336     this.sortFn = function(n1, n2){
18337         if(fs){
18338             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18339                 return 1;
18340             }
18341             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18342                 return -1;
18343             }
18344         }
18345         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18346         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18347         if(v1 < v2){
18348                         return dsc ? +1 : -1;
18349                 }else if(v1 > v2){
18350                         return dsc ? -1 : +1;
18351         }else{
18352                 return 0;
18353         }
18354     };
18355 };
18356
18357 Roo.tree.TreeSorter.prototype = {
18358     doSort : function(node){
18359         node.sort(this.sortFn);
18360     },
18361     
18362     compareNodes : function(n1, n2){
18363         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18364     },
18365     
18366     updateSort : function(tree, node){
18367         if(node.childrenRendered){
18368             this.doSort.defer(1, this, [node]);
18369         }
18370     }
18371 };/*
18372  * Based on:
18373  * Ext JS Library 1.1.1
18374  * Copyright(c) 2006-2007, Ext JS, LLC.
18375  *
18376  * Originally Released Under LGPL - original licence link has changed is not relivant.
18377  *
18378  * Fork - LGPL
18379  * <script type="text/javascript">
18380  */
18381
18382 if(Roo.dd.DropZone){
18383     
18384 Roo.tree.TreeDropZone = function(tree, config){
18385     this.allowParentInsert = false;
18386     this.allowContainerDrop = false;
18387     this.appendOnly = false;
18388     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18389     this.tree = tree;
18390     this.lastInsertClass = "x-tree-no-status";
18391     this.dragOverData = {};
18392 };
18393
18394 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18395     ddGroup : "TreeDD",
18396     scroll:  true,
18397     
18398     expandDelay : 1000,
18399     
18400     expandNode : function(node){
18401         if(node.hasChildNodes() && !node.isExpanded()){
18402             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18403         }
18404     },
18405     
18406     queueExpand : function(node){
18407         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18408     },
18409     
18410     cancelExpand : function(){
18411         if(this.expandProcId){
18412             clearTimeout(this.expandProcId);
18413             this.expandProcId = false;
18414         }
18415     },
18416     
18417     isValidDropPoint : function(n, pt, dd, e, data){
18418         if(!n || !data){ return false; }
18419         var targetNode = n.node;
18420         var dropNode = data.node;
18421         // default drop rules
18422         if(!(targetNode && targetNode.isTarget && pt)){
18423             return false;
18424         }
18425         if(pt == "append" && targetNode.allowChildren === false){
18426             return false;
18427         }
18428         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18429             return false;
18430         }
18431         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18432             return false;
18433         }
18434         // reuse the object
18435         var overEvent = this.dragOverData;
18436         overEvent.tree = this.tree;
18437         overEvent.target = targetNode;
18438         overEvent.data = data;
18439         overEvent.point = pt;
18440         overEvent.source = dd;
18441         overEvent.rawEvent = e;
18442         overEvent.dropNode = dropNode;
18443         overEvent.cancel = false;  
18444         var result = this.tree.fireEvent("nodedragover", overEvent);
18445         return overEvent.cancel === false && result !== false;
18446     },
18447     
18448     getDropPoint : function(e, n, dd)
18449     {
18450         var tn = n.node;
18451         if(tn.isRoot){
18452             return tn.allowChildren !== false ? "append" : false; // always append for root
18453         }
18454         var dragEl = n.ddel;
18455         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18456         var y = Roo.lib.Event.getPageY(e);
18457         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18458         
18459         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18460         var noAppend = tn.allowChildren === false;
18461         if(this.appendOnly || tn.parentNode.allowChildren === false){
18462             return noAppend ? false : "append";
18463         }
18464         var noBelow = false;
18465         if(!this.allowParentInsert){
18466             noBelow = tn.hasChildNodes() && tn.isExpanded();
18467         }
18468         var q = (b - t) / (noAppend ? 2 : 3);
18469         if(y >= t && y < (t + q)){
18470             return "above";
18471         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18472             return "below";
18473         }else{
18474             return "append";
18475         }
18476     },
18477     
18478     onNodeEnter : function(n, dd, e, data)
18479     {
18480         this.cancelExpand();
18481     },
18482     
18483     onNodeOver : function(n, dd, e, data)
18484     {
18485        
18486         var pt = this.getDropPoint(e, n, dd);
18487         var node = n.node;
18488         
18489         // auto node expand check
18490         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18491             this.queueExpand(node);
18492         }else if(pt != "append"){
18493             this.cancelExpand();
18494         }
18495         
18496         // set the insert point style on the target node
18497         var returnCls = this.dropNotAllowed;
18498         if(this.isValidDropPoint(n, pt, dd, e, data)){
18499            if(pt){
18500                var el = n.ddel;
18501                var cls;
18502                if(pt == "above"){
18503                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18504                    cls = "x-tree-drag-insert-above";
18505                }else if(pt == "below"){
18506                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18507                    cls = "x-tree-drag-insert-below";
18508                }else{
18509                    returnCls = "x-tree-drop-ok-append";
18510                    cls = "x-tree-drag-append";
18511                }
18512                if(this.lastInsertClass != cls){
18513                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18514                    this.lastInsertClass = cls;
18515                }
18516            }
18517        }
18518        return returnCls;
18519     },
18520     
18521     onNodeOut : function(n, dd, e, data){
18522         
18523         this.cancelExpand();
18524         this.removeDropIndicators(n);
18525     },
18526     
18527     onNodeDrop : function(n, dd, e, data){
18528         var point = this.getDropPoint(e, n, dd);
18529         var targetNode = n.node;
18530         targetNode.ui.startDrop();
18531         if(!this.isValidDropPoint(n, point, dd, e, data)){
18532             targetNode.ui.endDrop();
18533             return false;
18534         }
18535         // first try to find the drop node
18536         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18537         var dropEvent = {
18538             tree : this.tree,
18539             target: targetNode,
18540             data: data,
18541             point: point,
18542             source: dd,
18543             rawEvent: e,
18544             dropNode: dropNode,
18545             cancel: !dropNode   
18546         };
18547         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18548         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18549             targetNode.ui.endDrop();
18550             return false;
18551         }
18552         // allow target changing
18553         targetNode = dropEvent.target;
18554         if(point == "append" && !targetNode.isExpanded()){
18555             targetNode.expand(false, null, function(){
18556                 this.completeDrop(dropEvent);
18557             }.createDelegate(this));
18558         }else{
18559             this.completeDrop(dropEvent);
18560         }
18561         return true;
18562     },
18563     
18564     completeDrop : function(de){
18565         var ns = de.dropNode, p = de.point, t = de.target;
18566         if(!(ns instanceof Array)){
18567             ns = [ns];
18568         }
18569         var n;
18570         for(var i = 0, len = ns.length; i < len; i++){
18571             n = ns[i];
18572             if(p == "above"){
18573                 t.parentNode.insertBefore(n, t);
18574             }else if(p == "below"){
18575                 t.parentNode.insertBefore(n, t.nextSibling);
18576             }else{
18577                 t.appendChild(n);
18578             }
18579         }
18580         n.ui.focus();
18581         if(this.tree.hlDrop){
18582             n.ui.highlight();
18583         }
18584         t.ui.endDrop();
18585         this.tree.fireEvent("nodedrop", de);
18586     },
18587     
18588     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18589         if(this.tree.hlDrop){
18590             dropNode.ui.focus();
18591             dropNode.ui.highlight();
18592         }
18593         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18594     },
18595     
18596     getTree : function(){
18597         return this.tree;
18598     },
18599     
18600     removeDropIndicators : function(n){
18601         if(n && n.ddel){
18602             var el = n.ddel;
18603             Roo.fly(el).removeClass([
18604                     "x-tree-drag-insert-above",
18605                     "x-tree-drag-insert-below",
18606                     "x-tree-drag-append"]);
18607             this.lastInsertClass = "_noclass";
18608         }
18609     },
18610     
18611     beforeDragDrop : function(target, e, id){
18612         this.cancelExpand();
18613         return true;
18614     },
18615     
18616     afterRepair : function(data){
18617         if(data && Roo.enableFx){
18618             data.node.ui.highlight();
18619         }
18620         this.hideProxy();
18621     } 
18622     
18623 });
18624
18625 }
18626 /*
18627  * Based on:
18628  * Ext JS Library 1.1.1
18629  * Copyright(c) 2006-2007, Ext JS, LLC.
18630  *
18631  * Originally Released Under LGPL - original licence link has changed is not relivant.
18632  *
18633  * Fork - LGPL
18634  * <script type="text/javascript">
18635  */
18636  
18637
18638 if(Roo.dd.DragZone){
18639 Roo.tree.TreeDragZone = function(tree, config){
18640     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18641     this.tree = tree;
18642 };
18643
18644 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18645     ddGroup : "TreeDD",
18646    
18647     onBeforeDrag : function(data, e){
18648         var n = data.node;
18649         return n && n.draggable && !n.disabled;
18650     },
18651      
18652     
18653     onInitDrag : function(e){
18654         var data = this.dragData;
18655         this.tree.getSelectionModel().select(data.node);
18656         this.proxy.update("");
18657         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18658         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18659     },
18660     
18661     getRepairXY : function(e, data){
18662         return data.node.ui.getDDRepairXY();
18663     },
18664     
18665     onEndDrag : function(data, e){
18666         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18667         
18668         
18669     },
18670     
18671     onValidDrop : function(dd, e, id){
18672         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18673         this.hideProxy();
18674     },
18675     
18676     beforeInvalidDrop : function(e, id){
18677         // this scrolls the original position back into view
18678         var sm = this.tree.getSelectionModel();
18679         sm.clearSelections();
18680         sm.select(this.dragData.node);
18681     }
18682 });
18683 }/*
18684  * Based on:
18685  * Ext JS Library 1.1.1
18686  * Copyright(c) 2006-2007, Ext JS, LLC.
18687  *
18688  * Originally Released Under LGPL - original licence link has changed is not relivant.
18689  *
18690  * Fork - LGPL
18691  * <script type="text/javascript">
18692  */
18693 /**
18694  * @class Roo.tree.TreeEditor
18695  * @extends Roo.Editor
18696  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18697  * as the editor field.
18698  * @constructor
18699  * @param {Object} config (used to be the tree panel.)
18700  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18701  * 
18702  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18703  * @cfg {Roo.form.TextField|Object} field The field configuration
18704  *
18705  * 
18706  */
18707 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18708     var tree = config;
18709     var field;
18710     if (oldconfig) { // old style..
18711         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18712     } else {
18713         // new style..
18714         tree = config.tree;
18715         config.field = config.field  || {};
18716         config.field.xtype = 'TextField';
18717         field = Roo.factory(config.field, Roo.form);
18718     }
18719     config = config || {};
18720     
18721     
18722     this.addEvents({
18723         /**
18724          * @event beforenodeedit
18725          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18726          * false from the handler of this event.
18727          * @param {Editor} this
18728          * @param {Roo.tree.Node} node 
18729          */
18730         "beforenodeedit" : true
18731     });
18732     
18733     //Roo.log(config);
18734     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18735
18736     this.tree = tree;
18737
18738     tree.on('beforeclick', this.beforeNodeClick, this);
18739     tree.getTreeEl().on('mousedown', this.hide, this);
18740     this.on('complete', this.updateNode, this);
18741     this.on('beforestartedit', this.fitToTree, this);
18742     this.on('startedit', this.bindScroll, this, {delay:10});
18743     this.on('specialkey', this.onSpecialKey, this);
18744 };
18745
18746 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18747     /**
18748      * @cfg {String} alignment
18749      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18750      */
18751     alignment: "l-l",
18752     // inherit
18753     autoSize: false,
18754     /**
18755      * @cfg {Boolean} hideEl
18756      * True to hide the bound element while the editor is displayed (defaults to false)
18757      */
18758     hideEl : false,
18759     /**
18760      * @cfg {String} cls
18761      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18762      */
18763     cls: "x-small-editor x-tree-editor",
18764     /**
18765      * @cfg {Boolean} shim
18766      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18767      */
18768     shim:false,
18769     // inherit
18770     shadow:"frame",
18771     /**
18772      * @cfg {Number} maxWidth
18773      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18774      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18775      * scroll and client offsets into account prior to each edit.
18776      */
18777     maxWidth: 250,
18778
18779     editDelay : 350,
18780
18781     // private
18782     fitToTree : function(ed, el){
18783         var td = this.tree.getTreeEl().dom, nd = el.dom;
18784         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18785             td.scrollLeft = nd.offsetLeft;
18786         }
18787         var w = Math.min(
18788                 this.maxWidth,
18789                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18790         this.setSize(w, '');
18791         
18792         return this.fireEvent('beforenodeedit', this, this.editNode);
18793         
18794     },
18795
18796     // private
18797     triggerEdit : function(node){
18798         this.completeEdit();
18799         this.editNode = node;
18800         this.startEdit(node.ui.textNode, node.text);
18801     },
18802
18803     // private
18804     bindScroll : function(){
18805         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18806     },
18807
18808     // private
18809     beforeNodeClick : function(node, e){
18810         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18811         this.lastClick = new Date();
18812         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18813             e.stopEvent();
18814             this.triggerEdit(node);
18815             return false;
18816         }
18817         return true;
18818     },
18819
18820     // private
18821     updateNode : function(ed, value){
18822         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18823         this.editNode.setText(value);
18824     },
18825
18826     // private
18827     onHide : function(){
18828         Roo.tree.TreeEditor.superclass.onHide.call(this);
18829         if(this.editNode){
18830             this.editNode.ui.focus();
18831         }
18832     },
18833
18834     // private
18835     onSpecialKey : function(field, e){
18836         var k = e.getKey();
18837         if(k == e.ESC){
18838             e.stopEvent();
18839             this.cancelEdit();
18840         }else if(k == e.ENTER && !e.hasModifier()){
18841             e.stopEvent();
18842             this.completeEdit();
18843         }
18844     }
18845 });//<Script type="text/javascript">
18846 /*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856  
18857 /**
18858  * Not documented??? - probably should be...
18859  */
18860
18861 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18862     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18863     
18864     renderElements : function(n, a, targetNode, bulkRender){
18865         //consel.log("renderElements?");
18866         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18867
18868         var t = n.getOwnerTree();
18869         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18870         
18871         var cols = t.columns;
18872         var bw = t.borderWidth;
18873         var c = cols[0];
18874         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18875          var cb = typeof a.checked == "boolean";
18876         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18877         var colcls = 'x-t-' + tid + '-c0';
18878         var buf = [
18879             '<li class="x-tree-node">',
18880             
18881                 
18882                 '<div class="x-tree-node-el ', a.cls,'">',
18883                     // extran...
18884                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18885                 
18886                 
18887                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18888                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18889                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18890                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18891                            (a.iconCls ? ' '+a.iconCls : ''),
18892                            '" unselectable="on" />',
18893                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18894                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18895                              
18896                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18897                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18898                             '<span unselectable="on" qtip="' + tx + '">',
18899                              tx,
18900                              '</span></a>' ,
18901                     '</div>',
18902                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18903                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18904                  ];
18905         for(var i = 1, len = cols.length; i < len; i++){
18906             c = cols[i];
18907             colcls = 'x-t-' + tid + '-c' +i;
18908             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18909             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18910                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18911                       "</div>");
18912          }
18913          
18914          buf.push(
18915             '</a>',
18916             '<div class="x-clear"></div></div>',
18917             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18918             "</li>");
18919         
18920         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18921             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18922                                 n.nextSibling.ui.getEl(), buf.join(""));
18923         }else{
18924             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18925         }
18926         var el = this.wrap.firstChild;
18927         this.elRow = el;
18928         this.elNode = el.firstChild;
18929         this.ranchor = el.childNodes[1];
18930         this.ctNode = this.wrap.childNodes[1];
18931         var cs = el.firstChild.childNodes;
18932         this.indentNode = cs[0];
18933         this.ecNode = cs[1];
18934         this.iconNode = cs[2];
18935         var index = 3;
18936         if(cb){
18937             this.checkbox = cs[3];
18938             index++;
18939         }
18940         this.anchor = cs[index];
18941         
18942         this.textNode = cs[index].firstChild;
18943         
18944         //el.on("click", this.onClick, this);
18945         //el.on("dblclick", this.onDblClick, this);
18946         
18947         
18948        // console.log(this);
18949     },
18950     initEvents : function(){
18951         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18952         
18953             
18954         var a = this.ranchor;
18955
18956         var el = Roo.get(a);
18957
18958         if(Roo.isOpera){ // opera render bug ignores the CSS
18959             el.setStyle("text-decoration", "none");
18960         }
18961
18962         el.on("click", this.onClick, this);
18963         el.on("dblclick", this.onDblClick, this);
18964         el.on("contextmenu", this.onContextMenu, this);
18965         
18966     },
18967     
18968     /*onSelectedChange : function(state){
18969         if(state){
18970             this.focus();
18971             this.addClass("x-tree-selected");
18972         }else{
18973             //this.blur();
18974             this.removeClass("x-tree-selected");
18975         }
18976     },*/
18977     addClass : function(cls){
18978         if(this.elRow){
18979             Roo.fly(this.elRow).addClass(cls);
18980         }
18981         
18982     },
18983     
18984     
18985     removeClass : function(cls){
18986         if(this.elRow){
18987             Roo.fly(this.elRow).removeClass(cls);
18988         }
18989     }
18990
18991     
18992     
18993 });//<Script type="text/javascript">
18994
18995 /*
18996  * Based on:
18997  * Ext JS Library 1.1.1
18998  * Copyright(c) 2006-2007, Ext JS, LLC.
18999  *
19000  * Originally Released Under LGPL - original licence link has changed is not relivant.
19001  *
19002  * Fork - LGPL
19003  * <script type="text/javascript">
19004  */
19005  
19006
19007 /**
19008  * @class Roo.tree.ColumnTree
19009  * @extends Roo.data.TreePanel
19010  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19011  * @cfg {int} borderWidth  compined right/left border allowance
19012  * @constructor
19013  * @param {String/HTMLElement/Element} el The container element
19014  * @param {Object} config
19015  */
19016 Roo.tree.ColumnTree =  function(el, config)
19017 {
19018    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19019    this.addEvents({
19020         /**
19021         * @event resize
19022         * Fire this event on a container when it resizes
19023         * @param {int} w Width
19024         * @param {int} h Height
19025         */
19026        "resize" : true
19027     });
19028     this.on('resize', this.onResize, this);
19029 };
19030
19031 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19032     //lines:false,
19033     
19034     
19035     borderWidth: Roo.isBorderBox ? 0 : 2, 
19036     headEls : false,
19037     
19038     render : function(){
19039         // add the header.....
19040        
19041         Roo.tree.ColumnTree.superclass.render.apply(this);
19042         
19043         this.el.addClass('x-column-tree');
19044         
19045         this.headers = this.el.createChild(
19046             {cls:'x-tree-headers'},this.innerCt.dom);
19047    
19048         var cols = this.columns, c;
19049         var totalWidth = 0;
19050         this.headEls = [];
19051         var  len = cols.length;
19052         for(var i = 0; i < len; i++){
19053              c = cols[i];
19054              totalWidth += c.width;
19055             this.headEls.push(this.headers.createChild({
19056                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19057                  cn: {
19058                      cls:'x-tree-hd-text',
19059                      html: c.header
19060                  },
19061                  style:'width:'+(c.width-this.borderWidth)+'px;'
19062              }));
19063         }
19064         this.headers.createChild({cls:'x-clear'});
19065         // prevent floats from wrapping when clipped
19066         this.headers.setWidth(totalWidth);
19067         //this.innerCt.setWidth(totalWidth);
19068         this.innerCt.setStyle({ overflow: 'auto' });
19069         this.onResize(this.width, this.height);
19070              
19071         
19072     },
19073     onResize : function(w,h)
19074     {
19075         this.height = h;
19076         this.width = w;
19077         // resize cols..
19078         this.innerCt.setWidth(this.width);
19079         this.innerCt.setHeight(this.height-20);
19080         
19081         // headers...
19082         var cols = this.columns, c;
19083         var totalWidth = 0;
19084         var expEl = false;
19085         var len = cols.length;
19086         for(var i = 0; i < len; i++){
19087             c = cols[i];
19088             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19089                 // it's the expander..
19090                 expEl  = this.headEls[i];
19091                 continue;
19092             }
19093             totalWidth += c.width;
19094             
19095         }
19096         if (expEl) {
19097             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19098         }
19099         this.headers.setWidth(w-20);
19100
19101         
19102         
19103         
19104     }
19105 });
19106 /*
19107  * Based on:
19108  * Ext JS Library 1.1.1
19109  * Copyright(c) 2006-2007, Ext JS, LLC.
19110  *
19111  * Originally Released Under LGPL - original licence link has changed is not relivant.
19112  *
19113  * Fork - LGPL
19114  * <script type="text/javascript">
19115  */
19116  
19117 /**
19118  * @class Roo.menu.Menu
19119  * @extends Roo.util.Observable
19120  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19121  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19122  * @constructor
19123  * Creates a new Menu
19124  * @param {Object} config Configuration options
19125  */
19126 Roo.menu.Menu = function(config){
19127     Roo.apply(this, config);
19128     this.id = this.id || Roo.id();
19129     this.addEvents({
19130         /**
19131          * @event beforeshow
19132          * Fires before this menu is displayed
19133          * @param {Roo.menu.Menu} this
19134          */
19135         beforeshow : true,
19136         /**
19137          * @event beforehide
19138          * Fires before this menu is hidden
19139          * @param {Roo.menu.Menu} this
19140          */
19141         beforehide : true,
19142         /**
19143          * @event show
19144          * Fires after this menu is displayed
19145          * @param {Roo.menu.Menu} this
19146          */
19147         show : true,
19148         /**
19149          * @event hide
19150          * Fires after this menu is hidden
19151          * @param {Roo.menu.Menu} this
19152          */
19153         hide : true,
19154         /**
19155          * @event click
19156          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19157          * @param {Roo.menu.Menu} this
19158          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19159          * @param {Roo.EventObject} e
19160          */
19161         click : true,
19162         /**
19163          * @event mouseover
19164          * Fires when the mouse is hovering over this menu
19165          * @param {Roo.menu.Menu} this
19166          * @param {Roo.EventObject} e
19167          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19168          */
19169         mouseover : true,
19170         /**
19171          * @event mouseout
19172          * Fires when the mouse exits this menu
19173          * @param {Roo.menu.Menu} this
19174          * @param {Roo.EventObject} e
19175          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19176          */
19177         mouseout : true,
19178         /**
19179          * @event itemclick
19180          * Fires when a menu item contained in this menu is clicked
19181          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19182          * @param {Roo.EventObject} e
19183          */
19184         itemclick: true
19185     });
19186     if (this.registerMenu) {
19187         Roo.menu.MenuMgr.register(this);
19188     }
19189     
19190     var mis = this.items;
19191     this.items = new Roo.util.MixedCollection();
19192     if(mis){
19193         this.add.apply(this, mis);
19194     }
19195 };
19196
19197 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19198     /**
19199      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19200      */
19201     minWidth : 120,
19202     /**
19203      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19204      * for bottom-right shadow (defaults to "sides")
19205      */
19206     shadow : "sides",
19207     /**
19208      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19209      * this menu (defaults to "tl-tr?")
19210      */
19211     subMenuAlign : "tl-tr?",
19212     /**
19213      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19214      * relative to its element of origin (defaults to "tl-bl?")
19215      */
19216     defaultAlign : "tl-bl?",
19217     /**
19218      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19219      */
19220     allowOtherMenus : false,
19221     /**
19222      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19223      */
19224     registerMenu : true,
19225
19226     hidden:true,
19227
19228     // private
19229     render : function(){
19230         if(this.el){
19231             return;
19232         }
19233         var el = this.el = new Roo.Layer({
19234             cls: "x-menu",
19235             shadow:this.shadow,
19236             constrain: false,
19237             parentEl: this.parentEl || document.body,
19238             zindex:15000
19239         });
19240
19241         this.keyNav = new Roo.menu.MenuNav(this);
19242
19243         if(this.plain){
19244             el.addClass("x-menu-plain");
19245         }
19246         if(this.cls){
19247             el.addClass(this.cls);
19248         }
19249         // generic focus element
19250         this.focusEl = el.createChild({
19251             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19252         });
19253         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19254         //disabling touch- as it's causing issues ..
19255         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19256         ul.on('click'   , this.onClick, this);
19257         
19258         
19259         ul.on("mouseover", this.onMouseOver, this);
19260         ul.on("mouseout", this.onMouseOut, this);
19261         this.items.each(function(item){
19262             if (item.hidden) {
19263                 return;
19264             }
19265             
19266             var li = document.createElement("li");
19267             li.className = "x-menu-list-item";
19268             ul.dom.appendChild(li);
19269             item.render(li, this);
19270         }, this);
19271         this.ul = ul;
19272         this.autoWidth();
19273     },
19274
19275     // private
19276     autoWidth : function(){
19277         var el = this.el, ul = this.ul;
19278         if(!el){
19279             return;
19280         }
19281         var w = this.width;
19282         if(w){
19283             el.setWidth(w);
19284         }else if(Roo.isIE){
19285             el.setWidth(this.minWidth);
19286             var t = el.dom.offsetWidth; // force recalc
19287             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19288         }
19289     },
19290
19291     // private
19292     delayAutoWidth : function(){
19293         if(this.rendered){
19294             if(!this.awTask){
19295                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19296             }
19297             this.awTask.delay(20);
19298         }
19299     },
19300
19301     // private
19302     findTargetItem : function(e){
19303         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19304         if(t && t.menuItemId){
19305             return this.items.get(t.menuItemId);
19306         }
19307     },
19308
19309     // private
19310     onClick : function(e){
19311         Roo.log("menu.onClick");
19312         var t = this.findTargetItem(e);
19313         if(!t){
19314             return;
19315         }
19316         Roo.log(e);
19317         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19318             if(t == this.activeItem && t.shouldDeactivate(e)){
19319                 this.activeItem.deactivate();
19320                 delete this.activeItem;
19321                 return;
19322             }
19323             if(t.canActivate){
19324                 this.setActiveItem(t, true);
19325             }
19326             return;
19327             
19328             
19329         }
19330         
19331         t.onClick(e);
19332         this.fireEvent("click", this, t, e);
19333     },
19334
19335     // private
19336     setActiveItem : function(item, autoExpand){
19337         if(item != this.activeItem){
19338             if(this.activeItem){
19339                 this.activeItem.deactivate();
19340             }
19341             this.activeItem = item;
19342             item.activate(autoExpand);
19343         }else if(autoExpand){
19344             item.expandMenu();
19345         }
19346     },
19347
19348     // private
19349     tryActivate : function(start, step){
19350         var items = this.items;
19351         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19352             var item = items.get(i);
19353             if(!item.disabled && item.canActivate){
19354                 this.setActiveItem(item, false);
19355                 return item;
19356             }
19357         }
19358         return false;
19359     },
19360
19361     // private
19362     onMouseOver : function(e){
19363         var t;
19364         if(t = this.findTargetItem(e)){
19365             if(t.canActivate && !t.disabled){
19366                 this.setActiveItem(t, true);
19367             }
19368         }
19369         this.fireEvent("mouseover", this, e, t);
19370     },
19371
19372     // private
19373     onMouseOut : function(e){
19374         var t;
19375         if(t = this.findTargetItem(e)){
19376             if(t == this.activeItem && t.shouldDeactivate(e)){
19377                 this.activeItem.deactivate();
19378                 delete this.activeItem;
19379             }
19380         }
19381         this.fireEvent("mouseout", this, e, t);
19382     },
19383
19384     /**
19385      * Read-only.  Returns true if the menu is currently displayed, else false.
19386      * @type Boolean
19387      */
19388     isVisible : function(){
19389         return this.el && !this.hidden;
19390     },
19391
19392     /**
19393      * Displays this menu relative to another element
19394      * @param {String/HTMLElement/Roo.Element} element The element to align to
19395      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19396      * the element (defaults to this.defaultAlign)
19397      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19398      */
19399     show : function(el, pos, parentMenu){
19400         this.parentMenu = parentMenu;
19401         if(!this.el){
19402             this.render();
19403         }
19404         this.fireEvent("beforeshow", this);
19405         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19406     },
19407
19408     /**
19409      * Displays this menu at a specific xy position
19410      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19411      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19412      */
19413     showAt : function(xy, parentMenu, /* private: */_e){
19414         this.parentMenu = parentMenu;
19415         if(!this.el){
19416             this.render();
19417         }
19418         if(_e !== false){
19419             this.fireEvent("beforeshow", this);
19420             xy = this.el.adjustForConstraints(xy);
19421         }
19422         this.el.setXY(xy);
19423         this.el.show();
19424         this.hidden = false;
19425         this.focus();
19426         this.fireEvent("show", this);
19427     },
19428
19429     focus : function(){
19430         if(!this.hidden){
19431             this.doFocus.defer(50, this);
19432         }
19433     },
19434
19435     doFocus : function(){
19436         if(!this.hidden){
19437             this.focusEl.focus();
19438         }
19439     },
19440
19441     /**
19442      * Hides this menu and optionally all parent menus
19443      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19444      */
19445     hide : function(deep){
19446         if(this.el && this.isVisible()){
19447             this.fireEvent("beforehide", this);
19448             if(this.activeItem){
19449                 this.activeItem.deactivate();
19450                 this.activeItem = null;
19451             }
19452             this.el.hide();
19453             this.hidden = true;
19454             this.fireEvent("hide", this);
19455         }
19456         if(deep === true && this.parentMenu){
19457             this.parentMenu.hide(true);
19458         }
19459     },
19460
19461     /**
19462      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19463      * Any of the following are valid:
19464      * <ul>
19465      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19466      * <li>An HTMLElement object which will be converted to a menu item</li>
19467      * <li>A menu item config object that will be created as a new menu item</li>
19468      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19469      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19470      * </ul>
19471      * Usage:
19472      * <pre><code>
19473 // Create the menu
19474 var menu = new Roo.menu.Menu();
19475
19476 // Create a menu item to add by reference
19477 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19478
19479 // Add a bunch of items at once using different methods.
19480 // Only the last item added will be returned.
19481 var item = menu.add(
19482     menuItem,                // add existing item by ref
19483     'Dynamic Item',          // new TextItem
19484     '-',                     // new separator
19485     { text: 'Config Item' }  // new item by config
19486 );
19487 </code></pre>
19488      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19489      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19490      */
19491     add : function(){
19492         var a = arguments, l = a.length, item;
19493         for(var i = 0; i < l; i++){
19494             var el = a[i];
19495             if ((typeof(el) == "object") && el.xtype && el.xns) {
19496                 el = Roo.factory(el, Roo.menu);
19497             }
19498             
19499             if(el.render){ // some kind of Item
19500                 item = this.addItem(el);
19501             }else if(typeof el == "string"){ // string
19502                 if(el == "separator" || el == "-"){
19503                     item = this.addSeparator();
19504                 }else{
19505                     item = this.addText(el);
19506                 }
19507             }else if(el.tagName || el.el){ // element
19508                 item = this.addElement(el);
19509             }else if(typeof el == "object"){ // must be menu item config?
19510                 item = this.addMenuItem(el);
19511             }
19512         }
19513         return item;
19514     },
19515
19516     /**
19517      * Returns this menu's underlying {@link Roo.Element} object
19518      * @return {Roo.Element} The element
19519      */
19520     getEl : function(){
19521         if(!this.el){
19522             this.render();
19523         }
19524         return this.el;
19525     },
19526
19527     /**
19528      * Adds a separator bar to the menu
19529      * @return {Roo.menu.Item} The menu item that was added
19530      */
19531     addSeparator : function(){
19532         return this.addItem(new Roo.menu.Separator());
19533     },
19534
19535     /**
19536      * Adds an {@link Roo.Element} object to the menu
19537      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19538      * @return {Roo.menu.Item} The menu item that was added
19539      */
19540     addElement : function(el){
19541         return this.addItem(new Roo.menu.BaseItem(el));
19542     },
19543
19544     /**
19545      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19546      * @param {Roo.menu.Item} item The menu item to add
19547      * @return {Roo.menu.Item} The menu item that was added
19548      */
19549     addItem : function(item){
19550         this.items.add(item);
19551         if(this.ul){
19552             var li = document.createElement("li");
19553             li.className = "x-menu-list-item";
19554             this.ul.dom.appendChild(li);
19555             item.render(li, this);
19556             this.delayAutoWidth();
19557         }
19558         return item;
19559     },
19560
19561     /**
19562      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19563      * @param {Object} config A MenuItem config object
19564      * @return {Roo.menu.Item} The menu item that was added
19565      */
19566     addMenuItem : function(config){
19567         if(!(config instanceof Roo.menu.Item)){
19568             if(typeof config.checked == "boolean"){ // must be check menu item config?
19569                 config = new Roo.menu.CheckItem(config);
19570             }else{
19571                 config = new Roo.menu.Item(config);
19572             }
19573         }
19574         return this.addItem(config);
19575     },
19576
19577     /**
19578      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19579      * @param {String} text The text to display in the menu item
19580      * @return {Roo.menu.Item} The menu item that was added
19581      */
19582     addText : function(text){
19583         return this.addItem(new Roo.menu.TextItem({ text : text }));
19584     },
19585
19586     /**
19587      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19588      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19589      * @param {Roo.menu.Item} item The menu item to add
19590      * @return {Roo.menu.Item} The menu item that was added
19591      */
19592     insert : function(index, item){
19593         this.items.insert(index, item);
19594         if(this.ul){
19595             var li = document.createElement("li");
19596             li.className = "x-menu-list-item";
19597             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19598             item.render(li, this);
19599             this.delayAutoWidth();
19600         }
19601         return item;
19602     },
19603
19604     /**
19605      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19606      * @param {Roo.menu.Item} item The menu item to remove
19607      */
19608     remove : function(item){
19609         this.items.removeKey(item.id);
19610         item.destroy();
19611     },
19612
19613     /**
19614      * Removes and destroys all items in the menu
19615      */
19616     removeAll : function(){
19617         var f;
19618         while(f = this.items.first()){
19619             this.remove(f);
19620         }
19621     }
19622 });
19623
19624 // MenuNav is a private utility class used internally by the Menu
19625 Roo.menu.MenuNav = function(menu){
19626     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19627     this.scope = this.menu = menu;
19628 };
19629
19630 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19631     doRelay : function(e, h){
19632         var k = e.getKey();
19633         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19634             this.menu.tryActivate(0, 1);
19635             return false;
19636         }
19637         return h.call(this.scope || this, e, this.menu);
19638     },
19639
19640     up : function(e, m){
19641         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19642             m.tryActivate(m.items.length-1, -1);
19643         }
19644     },
19645
19646     down : function(e, m){
19647         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19648             m.tryActivate(0, 1);
19649         }
19650     },
19651
19652     right : function(e, m){
19653         if(m.activeItem){
19654             m.activeItem.expandMenu(true);
19655         }
19656     },
19657
19658     left : function(e, m){
19659         m.hide();
19660         if(m.parentMenu && m.parentMenu.activeItem){
19661             m.parentMenu.activeItem.activate();
19662         }
19663     },
19664
19665     enter : function(e, m){
19666         if(m.activeItem){
19667             e.stopPropagation();
19668             m.activeItem.onClick(e);
19669             m.fireEvent("click", this, m.activeItem);
19670             return true;
19671         }
19672     }
19673 });/*
19674  * Based on:
19675  * Ext JS Library 1.1.1
19676  * Copyright(c) 2006-2007, Ext JS, LLC.
19677  *
19678  * Originally Released Under LGPL - original licence link has changed is not relivant.
19679  *
19680  * Fork - LGPL
19681  * <script type="text/javascript">
19682  */
19683  
19684 /**
19685  * @class Roo.menu.MenuMgr
19686  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19687  * @singleton
19688  */
19689 Roo.menu.MenuMgr = function(){
19690    var menus, active, groups = {}, attached = false, lastShow = new Date();
19691
19692    // private - called when first menu is created
19693    function init(){
19694        menus = {};
19695        active = new Roo.util.MixedCollection();
19696        Roo.get(document).addKeyListener(27, function(){
19697            if(active.length > 0){
19698                hideAll();
19699            }
19700        });
19701    }
19702
19703    // private
19704    function hideAll(){
19705        if(active && active.length > 0){
19706            var c = active.clone();
19707            c.each(function(m){
19708                m.hide();
19709            });
19710        }
19711    }
19712
19713    // private
19714    function onHide(m){
19715        active.remove(m);
19716        if(active.length < 1){
19717            Roo.get(document).un("mousedown", onMouseDown);
19718            attached = false;
19719        }
19720    }
19721
19722    // private
19723    function onShow(m){
19724        var last = active.last();
19725        lastShow = new Date();
19726        active.add(m);
19727        if(!attached){
19728            Roo.get(document).on("mousedown", onMouseDown);
19729            attached = true;
19730        }
19731        if(m.parentMenu){
19732           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19733           m.parentMenu.activeChild = m;
19734        }else if(last && last.isVisible()){
19735           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19736        }
19737    }
19738
19739    // private
19740    function onBeforeHide(m){
19741        if(m.activeChild){
19742            m.activeChild.hide();
19743        }
19744        if(m.autoHideTimer){
19745            clearTimeout(m.autoHideTimer);
19746            delete m.autoHideTimer;
19747        }
19748    }
19749
19750    // private
19751    function onBeforeShow(m){
19752        var pm = m.parentMenu;
19753        if(!pm && !m.allowOtherMenus){
19754            hideAll();
19755        }else if(pm && pm.activeChild && active != m){
19756            pm.activeChild.hide();
19757        }
19758    }
19759
19760    // private
19761    function onMouseDown(e){
19762        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19763            hideAll();
19764        }
19765    }
19766
19767    // private
19768    function onBeforeCheck(mi, state){
19769        if(state){
19770            var g = groups[mi.group];
19771            for(var i = 0, l = g.length; i < l; i++){
19772                if(g[i] != mi){
19773                    g[i].setChecked(false);
19774                }
19775            }
19776        }
19777    }
19778
19779    return {
19780
19781        /**
19782         * Hides all menus that are currently visible
19783         */
19784        hideAll : function(){
19785             hideAll();  
19786        },
19787
19788        // private
19789        register : function(menu){
19790            if(!menus){
19791                init();
19792            }
19793            menus[menu.id] = menu;
19794            menu.on("beforehide", onBeforeHide);
19795            menu.on("hide", onHide);
19796            menu.on("beforeshow", onBeforeShow);
19797            menu.on("show", onShow);
19798            var g = menu.group;
19799            if(g && menu.events["checkchange"]){
19800                if(!groups[g]){
19801                    groups[g] = [];
19802                }
19803                groups[g].push(menu);
19804                menu.on("checkchange", onCheck);
19805            }
19806        },
19807
19808         /**
19809          * Returns a {@link Roo.menu.Menu} object
19810          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19811          * be used to generate and return a new Menu instance.
19812          */
19813        get : function(menu){
19814            if(typeof menu == "string"){ // menu id
19815                return menus[menu];
19816            }else if(menu.events){  // menu instance
19817                return menu;
19818            }else if(typeof menu.length == 'number'){ // array of menu items?
19819                return new Roo.menu.Menu({items:menu});
19820            }else{ // otherwise, must be a config
19821                return new Roo.menu.Menu(menu);
19822            }
19823        },
19824
19825        // private
19826        unregister : function(menu){
19827            delete menus[menu.id];
19828            menu.un("beforehide", onBeforeHide);
19829            menu.un("hide", onHide);
19830            menu.un("beforeshow", onBeforeShow);
19831            menu.un("show", onShow);
19832            var g = menu.group;
19833            if(g && menu.events["checkchange"]){
19834                groups[g].remove(menu);
19835                menu.un("checkchange", onCheck);
19836            }
19837        },
19838
19839        // private
19840        registerCheckable : function(menuItem){
19841            var g = menuItem.group;
19842            if(g){
19843                if(!groups[g]){
19844                    groups[g] = [];
19845                }
19846                groups[g].push(menuItem);
19847                menuItem.on("beforecheckchange", onBeforeCheck);
19848            }
19849        },
19850
19851        // private
19852        unregisterCheckable : function(menuItem){
19853            var g = menuItem.group;
19854            if(g){
19855                groups[g].remove(menuItem);
19856                menuItem.un("beforecheckchange", onBeforeCheck);
19857            }
19858        }
19859    };
19860 }();/*
19861  * Based on:
19862  * Ext JS Library 1.1.1
19863  * Copyright(c) 2006-2007, Ext JS, LLC.
19864  *
19865  * Originally Released Under LGPL - original licence link has changed is not relivant.
19866  *
19867  * Fork - LGPL
19868  * <script type="text/javascript">
19869  */
19870  
19871
19872 /**
19873  * @class Roo.menu.BaseItem
19874  * @extends Roo.Component
19875  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19876  * management and base configuration options shared by all menu components.
19877  * @constructor
19878  * Creates a new BaseItem
19879  * @param {Object} config Configuration options
19880  */
19881 Roo.menu.BaseItem = function(config){
19882     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19883
19884     this.addEvents({
19885         /**
19886          * @event click
19887          * Fires when this item is clicked
19888          * @param {Roo.menu.BaseItem} this
19889          * @param {Roo.EventObject} e
19890          */
19891         click: true,
19892         /**
19893          * @event activate
19894          * Fires when this item is activated
19895          * @param {Roo.menu.BaseItem} this
19896          */
19897         activate : true,
19898         /**
19899          * @event deactivate
19900          * Fires when this item is deactivated
19901          * @param {Roo.menu.BaseItem} this
19902          */
19903         deactivate : true
19904     });
19905
19906     if(this.handler){
19907         this.on("click", this.handler, this.scope, true);
19908     }
19909 };
19910
19911 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19912     /**
19913      * @cfg {Function} handler
19914      * A function that will handle the click event of this menu item (defaults to undefined)
19915      */
19916     /**
19917      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19918      */
19919     canActivate : false,
19920     
19921      /**
19922      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19923      */
19924     hidden: false,
19925     
19926     /**
19927      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19928      */
19929     activeClass : "x-menu-item-active",
19930     /**
19931      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19932      */
19933     hideOnClick : true,
19934     /**
19935      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19936      */
19937     hideDelay : 100,
19938
19939     // private
19940     ctype: "Roo.menu.BaseItem",
19941
19942     // private
19943     actionMode : "container",
19944
19945     // private
19946     render : function(container, parentMenu){
19947         this.parentMenu = parentMenu;
19948         Roo.menu.BaseItem.superclass.render.call(this, container);
19949         this.container.menuItemId = this.id;
19950     },
19951
19952     // private
19953     onRender : function(container, position){
19954         this.el = Roo.get(this.el);
19955         container.dom.appendChild(this.el.dom);
19956     },
19957
19958     // private
19959     onClick : function(e){
19960         if(!this.disabled && this.fireEvent("click", this, e) !== false
19961                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19962             this.handleClick(e);
19963         }else{
19964             e.stopEvent();
19965         }
19966     },
19967
19968     // private
19969     activate : function(){
19970         if(this.disabled){
19971             return false;
19972         }
19973         var li = this.container;
19974         li.addClass(this.activeClass);
19975         this.region = li.getRegion().adjust(2, 2, -2, -2);
19976         this.fireEvent("activate", this);
19977         return true;
19978     },
19979
19980     // private
19981     deactivate : function(){
19982         this.container.removeClass(this.activeClass);
19983         this.fireEvent("deactivate", this);
19984     },
19985
19986     // private
19987     shouldDeactivate : function(e){
19988         return !this.region || !this.region.contains(e.getPoint());
19989     },
19990
19991     // private
19992     handleClick : function(e){
19993         if(this.hideOnClick){
19994             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19995         }
19996     },
19997
19998     // private
19999     expandMenu : function(autoActivate){
20000         // do nothing
20001     },
20002
20003     // private
20004     hideMenu : function(){
20005         // do nothing
20006     }
20007 });/*
20008  * Based on:
20009  * Ext JS Library 1.1.1
20010  * Copyright(c) 2006-2007, Ext JS, LLC.
20011  *
20012  * Originally Released Under LGPL - original licence link has changed is not relivant.
20013  *
20014  * Fork - LGPL
20015  * <script type="text/javascript">
20016  */
20017  
20018 /**
20019  * @class Roo.menu.Adapter
20020  * @extends Roo.menu.BaseItem
20021  * 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.
20022  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20023  * @constructor
20024  * Creates a new Adapter
20025  * @param {Object} config Configuration options
20026  */
20027 Roo.menu.Adapter = function(component, config){
20028     Roo.menu.Adapter.superclass.constructor.call(this, config);
20029     this.component = component;
20030 };
20031 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20032     // private
20033     canActivate : true,
20034
20035     // private
20036     onRender : function(container, position){
20037         this.component.render(container);
20038         this.el = this.component.getEl();
20039     },
20040
20041     // private
20042     activate : function(){
20043         if(this.disabled){
20044             return false;
20045         }
20046         this.component.focus();
20047         this.fireEvent("activate", this);
20048         return true;
20049     },
20050
20051     // private
20052     deactivate : function(){
20053         this.fireEvent("deactivate", this);
20054     },
20055
20056     // private
20057     disable : function(){
20058         this.component.disable();
20059         Roo.menu.Adapter.superclass.disable.call(this);
20060     },
20061
20062     // private
20063     enable : function(){
20064         this.component.enable();
20065         Roo.menu.Adapter.superclass.enable.call(this);
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.TextItem
20080  * @extends Roo.menu.BaseItem
20081  * Adds a static text string to a menu, usually used as either a heading or group separator.
20082  * Note: old style constructor with text is still supported.
20083  * 
20084  * @constructor
20085  * Creates a new TextItem
20086  * @param {Object} cfg Configuration
20087  */
20088 Roo.menu.TextItem = function(cfg){
20089     if (typeof(cfg) == 'string') {
20090         this.text = cfg;
20091     } else {
20092         Roo.apply(this,cfg);
20093     }
20094     
20095     Roo.menu.TextItem.superclass.constructor.call(this);
20096 };
20097
20098 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20099     /**
20100      * @cfg {Boolean} text Text to show on item.
20101      */
20102     text : '',
20103     
20104     /**
20105      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20106      */
20107     hideOnClick : false,
20108     /**
20109      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20110      */
20111     itemCls : "x-menu-text",
20112
20113     // private
20114     onRender : function(){
20115         var s = document.createElement("span");
20116         s.className = this.itemCls;
20117         s.innerHTML = this.text;
20118         this.el = s;
20119         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20120     }
20121 });/*
20122  * Based on:
20123  * Ext JS Library 1.1.1
20124  * Copyright(c) 2006-2007, Ext JS, LLC.
20125  *
20126  * Originally Released Under LGPL - original licence link has changed is not relivant.
20127  *
20128  * Fork - LGPL
20129  * <script type="text/javascript">
20130  */
20131
20132 /**
20133  * @class Roo.menu.Separator
20134  * @extends Roo.menu.BaseItem
20135  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20136  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20137  * @constructor
20138  * @param {Object} config Configuration options
20139  */
20140 Roo.menu.Separator = function(config){
20141     Roo.menu.Separator.superclass.constructor.call(this, config);
20142 };
20143
20144 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20145     /**
20146      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20147      */
20148     itemCls : "x-menu-sep",
20149     /**
20150      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20151      */
20152     hideOnClick : false,
20153
20154     // private
20155     onRender : function(li){
20156         var s = document.createElement("span");
20157         s.className = this.itemCls;
20158         s.innerHTML = "&#160;";
20159         this.el = s;
20160         li.addClass("x-menu-sep-li");
20161         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20162     }
20163 });/*
20164  * Based on:
20165  * Ext JS Library 1.1.1
20166  * Copyright(c) 2006-2007, Ext JS, LLC.
20167  *
20168  * Originally Released Under LGPL - original licence link has changed is not relivant.
20169  *
20170  * Fork - LGPL
20171  * <script type="text/javascript">
20172  */
20173 /**
20174  * @class Roo.menu.Item
20175  * @extends Roo.menu.BaseItem
20176  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20177  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20178  * activation and click handling.
20179  * @constructor
20180  * Creates a new Item
20181  * @param {Object} config Configuration options
20182  */
20183 Roo.menu.Item = function(config){
20184     Roo.menu.Item.superclass.constructor.call(this, config);
20185     if(this.menu){
20186         this.menu = Roo.menu.MenuMgr.get(this.menu);
20187     }
20188 };
20189 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20190     
20191     /**
20192      * @cfg {String} text
20193      * The text to show on the menu item.
20194      */
20195     text: '',
20196      /**
20197      * @cfg {String} HTML to render in menu
20198      * The text to show on the menu item (HTML version).
20199      */
20200     html: '',
20201     /**
20202      * @cfg {String} icon
20203      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20204      */
20205     icon: undefined,
20206     /**
20207      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20208      */
20209     itemCls : "x-menu-item",
20210     /**
20211      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20212      */
20213     canActivate : true,
20214     /**
20215      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20216      */
20217     showDelay: 200,
20218     // doc'd in BaseItem
20219     hideDelay: 200,
20220
20221     // private
20222     ctype: "Roo.menu.Item",
20223     
20224     // private
20225     onRender : function(container, position){
20226         var el = document.createElement("a");
20227         el.hideFocus = true;
20228         el.unselectable = "on";
20229         el.href = this.href || "#";
20230         if(this.hrefTarget){
20231             el.target = this.hrefTarget;
20232         }
20233         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20234         
20235         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20236         
20237         el.innerHTML = String.format(
20238                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20239                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20240         this.el = el;
20241         Roo.menu.Item.superclass.onRender.call(this, container, position);
20242     },
20243
20244     /**
20245      * Sets the text to display in this menu item
20246      * @param {String} text The text to display
20247      * @param {Boolean} isHTML true to indicate text is pure html.
20248      */
20249     setText : function(text, isHTML){
20250         if (isHTML) {
20251             this.html = text;
20252         } else {
20253             this.text = text;
20254             this.html = '';
20255         }
20256         if(this.rendered){
20257             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20258      
20259             this.el.update(String.format(
20260                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20261                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20262             this.parentMenu.autoWidth();
20263         }
20264     },
20265
20266     // private
20267     handleClick : function(e){
20268         if(!this.href){ // if no link defined, stop the event automatically
20269             e.stopEvent();
20270         }
20271         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20272     },
20273
20274     // private
20275     activate : function(autoExpand){
20276         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20277             this.focus();
20278             if(autoExpand){
20279                 this.expandMenu();
20280             }
20281         }
20282         return true;
20283     },
20284
20285     // private
20286     shouldDeactivate : function(e){
20287         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20288             if(this.menu && this.menu.isVisible()){
20289                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20290             }
20291             return true;
20292         }
20293         return false;
20294     },
20295
20296     // private
20297     deactivate : function(){
20298         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20299         this.hideMenu();
20300     },
20301
20302     // private
20303     expandMenu : function(autoActivate){
20304         if(!this.disabled && this.menu){
20305             clearTimeout(this.hideTimer);
20306             delete this.hideTimer;
20307             if(!this.menu.isVisible() && !this.showTimer){
20308                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20309             }else if (this.menu.isVisible() && autoActivate){
20310                 this.menu.tryActivate(0, 1);
20311             }
20312         }
20313     },
20314
20315     // private
20316     deferExpand : function(autoActivate){
20317         delete this.showTimer;
20318         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20319         if(autoActivate){
20320             this.menu.tryActivate(0, 1);
20321         }
20322     },
20323
20324     // private
20325     hideMenu : function(){
20326         clearTimeout(this.showTimer);
20327         delete this.showTimer;
20328         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20329             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20330         }
20331     },
20332
20333     // private
20334     deferHide : function(){
20335         delete this.hideTimer;
20336         this.menu.hide();
20337     }
20338 });/*
20339  * Based on:
20340  * Ext JS Library 1.1.1
20341  * Copyright(c) 2006-2007, Ext JS, LLC.
20342  *
20343  * Originally Released Under LGPL - original licence link has changed is not relivant.
20344  *
20345  * Fork - LGPL
20346  * <script type="text/javascript">
20347  */
20348  
20349 /**
20350  * @class Roo.menu.CheckItem
20351  * @extends Roo.menu.Item
20352  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20353  * @constructor
20354  * Creates a new CheckItem
20355  * @param {Object} config Configuration options
20356  */
20357 Roo.menu.CheckItem = function(config){
20358     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20359     this.addEvents({
20360         /**
20361          * @event beforecheckchange
20362          * Fires before the checked value is set, providing an opportunity to cancel if needed
20363          * @param {Roo.menu.CheckItem} this
20364          * @param {Boolean} checked The new checked value that will be set
20365          */
20366         "beforecheckchange" : true,
20367         /**
20368          * @event checkchange
20369          * Fires after the checked value has been set
20370          * @param {Roo.menu.CheckItem} this
20371          * @param {Boolean} checked The checked value that was set
20372          */
20373         "checkchange" : true
20374     });
20375     if(this.checkHandler){
20376         this.on('checkchange', this.checkHandler, this.scope);
20377     }
20378 };
20379 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20380     /**
20381      * @cfg {String} group
20382      * All check items with the same group name will automatically be grouped into a single-select
20383      * radio button group (defaults to '')
20384      */
20385     /**
20386      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20387      */
20388     itemCls : "x-menu-item x-menu-check-item",
20389     /**
20390      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20391      */
20392     groupClass : "x-menu-group-item",
20393
20394     /**
20395      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20396      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20397      * initialized with checked = true will be rendered as checked.
20398      */
20399     checked: false,
20400
20401     // private
20402     ctype: "Roo.menu.CheckItem",
20403
20404     // private
20405     onRender : function(c){
20406         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20407         if(this.group){
20408             this.el.addClass(this.groupClass);
20409         }
20410         Roo.menu.MenuMgr.registerCheckable(this);
20411         if(this.checked){
20412             this.checked = false;
20413             this.setChecked(true, true);
20414         }
20415     },
20416
20417     // private
20418     destroy : function(){
20419         if(this.rendered){
20420             Roo.menu.MenuMgr.unregisterCheckable(this);
20421         }
20422         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20423     },
20424
20425     /**
20426      * Set the checked state of this item
20427      * @param {Boolean} checked The new checked value
20428      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20429      */
20430     setChecked : function(state, suppressEvent){
20431         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20432             if(this.container){
20433                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20434             }
20435             this.checked = state;
20436             if(suppressEvent !== true){
20437                 this.fireEvent("checkchange", this, state);
20438             }
20439         }
20440     },
20441
20442     // private
20443     handleClick : function(e){
20444        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20445            this.setChecked(!this.checked);
20446        }
20447        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20448     }
20449 });/*
20450  * Based on:
20451  * Ext JS Library 1.1.1
20452  * Copyright(c) 2006-2007, Ext JS, LLC.
20453  *
20454  * Originally Released Under LGPL - original licence link has changed is not relivant.
20455  *
20456  * Fork - LGPL
20457  * <script type="text/javascript">
20458  */
20459  
20460 /**
20461  * @class Roo.menu.DateItem
20462  * @extends Roo.menu.Adapter
20463  * A menu item that wraps the {@link Roo.DatPicker} component.
20464  * @constructor
20465  * Creates a new DateItem
20466  * @param {Object} config Configuration options
20467  */
20468 Roo.menu.DateItem = function(config){
20469     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20470     /** The Roo.DatePicker object @type Roo.DatePicker */
20471     this.picker = this.component;
20472     this.addEvents({select: true});
20473     
20474     this.picker.on("render", function(picker){
20475         picker.getEl().swallowEvent("click");
20476         picker.container.addClass("x-menu-date-item");
20477     });
20478
20479     this.picker.on("select", this.onSelect, this);
20480 };
20481
20482 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20483     // private
20484     onSelect : function(picker, date){
20485         this.fireEvent("select", this, date, picker);
20486         Roo.menu.DateItem.superclass.handleClick.call(this);
20487     }
20488 });/*
20489  * Based on:
20490  * Ext JS Library 1.1.1
20491  * Copyright(c) 2006-2007, Ext JS, LLC.
20492  *
20493  * Originally Released Under LGPL - original licence link has changed is not relivant.
20494  *
20495  * Fork - LGPL
20496  * <script type="text/javascript">
20497  */
20498  
20499 /**
20500  * @class Roo.menu.ColorItem
20501  * @extends Roo.menu.Adapter
20502  * A menu item that wraps the {@link Roo.ColorPalette} component.
20503  * @constructor
20504  * Creates a new ColorItem
20505  * @param {Object} config Configuration options
20506  */
20507 Roo.menu.ColorItem = function(config){
20508     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20509     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20510     this.palette = this.component;
20511     this.relayEvents(this.palette, ["select"]);
20512     if(this.selectHandler){
20513         this.on('select', this.selectHandler, this.scope);
20514     }
20515 };
20516 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20517  * Based on:
20518  * Ext JS Library 1.1.1
20519  * Copyright(c) 2006-2007, Ext JS, LLC.
20520  *
20521  * Originally Released Under LGPL - original licence link has changed is not relivant.
20522  *
20523  * Fork - LGPL
20524  * <script type="text/javascript">
20525  */
20526  
20527
20528 /**
20529  * @class Roo.menu.DateMenu
20530  * @extends Roo.menu.Menu
20531  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20532  * @constructor
20533  * Creates a new DateMenu
20534  * @param {Object} config Configuration options
20535  */
20536 Roo.menu.DateMenu = function(config){
20537     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20538     this.plain = true;
20539     var di = new Roo.menu.DateItem(config);
20540     this.add(di);
20541     /**
20542      * The {@link Roo.DatePicker} instance for this DateMenu
20543      * @type DatePicker
20544      */
20545     this.picker = di.picker;
20546     /**
20547      * @event select
20548      * @param {DatePicker} picker
20549      * @param {Date} date
20550      */
20551     this.relayEvents(di, ["select"]);
20552     this.on('beforeshow', function(){
20553         if(this.picker){
20554             this.picker.hideMonthPicker(false);
20555         }
20556     }, this);
20557 };
20558 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20559     cls:'x-date-menu'
20560 });/*
20561  * Based on:
20562  * Ext JS Library 1.1.1
20563  * Copyright(c) 2006-2007, Ext JS, LLC.
20564  *
20565  * Originally Released Under LGPL - original licence link has changed is not relivant.
20566  *
20567  * Fork - LGPL
20568  * <script type="text/javascript">
20569  */
20570  
20571
20572 /**
20573  * @class Roo.menu.ColorMenu
20574  * @extends Roo.menu.Menu
20575  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20576  * @constructor
20577  * Creates a new ColorMenu
20578  * @param {Object} config Configuration options
20579  */
20580 Roo.menu.ColorMenu = function(config){
20581     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20582     this.plain = true;
20583     var ci = new Roo.menu.ColorItem(config);
20584     this.add(ci);
20585     /**
20586      * The {@link Roo.ColorPalette} instance for this ColorMenu
20587      * @type ColorPalette
20588      */
20589     this.palette = ci.palette;
20590     /**
20591      * @event select
20592      * @param {ColorPalette} palette
20593      * @param {String} color
20594      */
20595     this.relayEvents(ci, ["select"]);
20596 };
20597 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20598  * Based on:
20599  * Ext JS Library 1.1.1
20600  * Copyright(c) 2006-2007, Ext JS, LLC.
20601  *
20602  * Originally Released Under LGPL - original licence link has changed is not relivant.
20603  *
20604  * Fork - LGPL
20605  * <script type="text/javascript">
20606  */
20607  
20608 /**
20609  * @class Roo.form.Field
20610  * @extends Roo.BoxComponent
20611  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20612  * @constructor
20613  * Creates a new Field
20614  * @param {Object} config Configuration options
20615  */
20616 Roo.form.Field = function(config){
20617     Roo.form.Field.superclass.constructor.call(this, config);
20618 };
20619
20620 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20621     /**
20622      * @cfg {String} fieldLabel Label to use when rendering a form.
20623      */
20624        /**
20625      * @cfg {String} qtip Mouse over tip
20626      */
20627      
20628     /**
20629      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20630      */
20631     invalidClass : "x-form-invalid",
20632     /**
20633      * @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")
20634      */
20635     invalidText : "The value in this field is invalid",
20636     /**
20637      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20638      */
20639     focusClass : "x-form-focus",
20640     /**
20641      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20642       automatic validation (defaults to "keyup").
20643      */
20644     validationEvent : "keyup",
20645     /**
20646      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20647      */
20648     validateOnBlur : true,
20649     /**
20650      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20651      */
20652     validationDelay : 250,
20653     /**
20654      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20655      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20656      */
20657     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20658     /**
20659      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20660      */
20661     fieldClass : "x-form-field",
20662     /**
20663      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20664      *<pre>
20665 Value         Description
20666 -----------   ----------------------------------------------------------------------
20667 qtip          Display a quick tip when the user hovers over the field
20668 title         Display a default browser title attribute popup
20669 under         Add a block div beneath the field containing the error text
20670 side          Add an error icon to the right of the field with a popup on hover
20671 [element id]  Add the error text directly to the innerHTML of the specified element
20672 </pre>
20673      */
20674     msgTarget : 'qtip',
20675     /**
20676      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20677      */
20678     msgFx : 'normal',
20679
20680     /**
20681      * @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.
20682      */
20683     readOnly : false,
20684
20685     /**
20686      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20687      */
20688     disabled : false,
20689
20690     /**
20691      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20692      */
20693     inputType : undefined,
20694     
20695     /**
20696      * @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).
20697          */
20698         tabIndex : undefined,
20699         
20700     // private
20701     isFormField : true,
20702
20703     // private
20704     hasFocus : false,
20705     /**
20706      * @property {Roo.Element} fieldEl
20707      * Element Containing the rendered Field (with label etc.)
20708      */
20709     /**
20710      * @cfg {Mixed} value A value to initialize this field with.
20711      */
20712     value : undefined,
20713
20714     /**
20715      * @cfg {String} name The field's HTML name attribute.
20716      */
20717     /**
20718      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20719      */
20720
20721         // private ??
20722         initComponent : function(){
20723         Roo.form.Field.superclass.initComponent.call(this);
20724         this.addEvents({
20725             /**
20726              * @event focus
20727              * Fires when this field receives input focus.
20728              * @param {Roo.form.Field} this
20729              */
20730             focus : true,
20731             /**
20732              * @event blur
20733              * Fires when this field loses input focus.
20734              * @param {Roo.form.Field} this
20735              */
20736             blur : true,
20737             /**
20738              * @event specialkey
20739              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20740              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20741              * @param {Roo.form.Field} this
20742              * @param {Roo.EventObject} e The event object
20743              */
20744             specialkey : true,
20745             /**
20746              * @event change
20747              * Fires just before the field blurs if the field value has changed.
20748              * @param {Roo.form.Field} this
20749              * @param {Mixed} newValue The new value
20750              * @param {Mixed} oldValue The original value
20751              */
20752             change : true,
20753             /**
20754              * @event invalid
20755              * Fires after the field has been marked as invalid.
20756              * @param {Roo.form.Field} this
20757              * @param {String} msg The validation message
20758              */
20759             invalid : true,
20760             /**
20761              * @event valid
20762              * Fires after the field has been validated with no errors.
20763              * @param {Roo.form.Field} this
20764              */
20765             valid : true,
20766              /**
20767              * @event keyup
20768              * Fires after the key up
20769              * @param {Roo.form.Field} this
20770              * @param {Roo.EventObject}  e The event Object
20771              */
20772             keyup : true
20773         });
20774     },
20775
20776     /**
20777      * Returns the name attribute of the field if available
20778      * @return {String} name The field name
20779      */
20780     getName: function(){
20781          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20782     },
20783
20784     // private
20785     onRender : function(ct, position){
20786         Roo.form.Field.superclass.onRender.call(this, ct, position);
20787         if(!this.el){
20788             var cfg = this.getAutoCreate();
20789             if(!cfg.name){
20790                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20791             }
20792             if (!cfg.name.length) {
20793                 delete cfg.name;
20794             }
20795             if(this.inputType){
20796                 cfg.type = this.inputType;
20797             }
20798             this.el = ct.createChild(cfg, position);
20799         }
20800         var type = this.el.dom.type;
20801         if(type){
20802             if(type == 'password'){
20803                 type = 'text';
20804             }
20805             this.el.addClass('x-form-'+type);
20806         }
20807         if(this.readOnly){
20808             this.el.dom.readOnly = true;
20809         }
20810         if(this.tabIndex !== undefined){
20811             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20812         }
20813
20814         this.el.addClass([this.fieldClass, this.cls]);
20815         this.initValue();
20816     },
20817
20818     /**
20819      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20820      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20821      * @return {Roo.form.Field} this
20822      */
20823     applyTo : function(target){
20824         this.allowDomMove = false;
20825         this.el = Roo.get(target);
20826         this.render(this.el.dom.parentNode);
20827         return this;
20828     },
20829
20830     // private
20831     initValue : function(){
20832         if(this.value !== undefined){
20833             this.setValue(this.value);
20834         }else if(this.el.dom.value.length > 0){
20835             this.setValue(this.el.dom.value);
20836         }
20837     },
20838
20839     /**
20840      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20841      */
20842     isDirty : function() {
20843         if(this.disabled) {
20844             return false;
20845         }
20846         return String(this.getValue()) !== String(this.originalValue);
20847     },
20848
20849     // private
20850     afterRender : function(){
20851         Roo.form.Field.superclass.afterRender.call(this);
20852         this.initEvents();
20853     },
20854
20855     // private
20856     fireKey : function(e){
20857         //Roo.log('field ' + e.getKey());
20858         if(e.isNavKeyPress()){
20859             this.fireEvent("specialkey", this, e);
20860         }
20861     },
20862
20863     /**
20864      * Resets the current field value to the originally loaded value and clears any validation messages
20865      */
20866     reset : function(){
20867         this.setValue(this.resetValue);
20868         this.clearInvalid();
20869     },
20870
20871     // private
20872     initEvents : function(){
20873         // safari killled keypress - so keydown is now used..
20874         this.el.on("keydown" , this.fireKey,  this);
20875         this.el.on("focus", this.onFocus,  this);
20876         this.el.on("blur", this.onBlur,  this);
20877         this.el.relayEvent('keyup', this);
20878
20879         // reference to original value for reset
20880         this.originalValue = this.getValue();
20881         this.resetValue =  this.getValue();
20882     },
20883
20884     // private
20885     onFocus : function(){
20886         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20887             this.el.addClass(this.focusClass);
20888         }
20889         if(!this.hasFocus){
20890             this.hasFocus = true;
20891             this.startValue = this.getValue();
20892             this.fireEvent("focus", this);
20893         }
20894     },
20895
20896     beforeBlur : Roo.emptyFn,
20897
20898     // private
20899     onBlur : function(){
20900         this.beforeBlur();
20901         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20902             this.el.removeClass(this.focusClass);
20903         }
20904         this.hasFocus = false;
20905         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20906             this.validate();
20907         }
20908         var v = this.getValue();
20909         if(String(v) !== String(this.startValue)){
20910             this.fireEvent('change', this, v, this.startValue);
20911         }
20912         this.fireEvent("blur", this);
20913     },
20914
20915     /**
20916      * Returns whether or not the field value is currently valid
20917      * @param {Boolean} preventMark True to disable marking the field invalid
20918      * @return {Boolean} True if the value is valid, else false
20919      */
20920     isValid : function(preventMark){
20921         if(this.disabled){
20922             return true;
20923         }
20924         var restore = this.preventMark;
20925         this.preventMark = preventMark === true;
20926         var v = this.validateValue(this.processValue(this.getRawValue()));
20927         this.preventMark = restore;
20928         return v;
20929     },
20930
20931     /**
20932      * Validates the field value
20933      * @return {Boolean} True if the value is valid, else false
20934      */
20935     validate : function(){
20936         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20937             this.clearInvalid();
20938             return true;
20939         }
20940         return false;
20941     },
20942
20943     processValue : function(value){
20944         return value;
20945     },
20946
20947     // private
20948     // Subclasses should provide the validation implementation by overriding this
20949     validateValue : function(value){
20950         return true;
20951     },
20952
20953     /**
20954      * Mark this field as invalid
20955      * @param {String} msg The validation message
20956      */
20957     markInvalid : function(msg){
20958         if(!this.rendered || this.preventMark){ // not rendered
20959             return;
20960         }
20961         
20962         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20963         
20964         obj.el.addClass(this.invalidClass);
20965         msg = msg || this.invalidText;
20966         switch(this.msgTarget){
20967             case 'qtip':
20968                 obj.el.dom.qtip = msg;
20969                 obj.el.dom.qclass = 'x-form-invalid-tip';
20970                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20971                     Roo.QuickTips.enable();
20972                 }
20973                 break;
20974             case 'title':
20975                 this.el.dom.title = msg;
20976                 break;
20977             case 'under':
20978                 if(!this.errorEl){
20979                     var elp = this.el.findParent('.x-form-element', 5, true);
20980                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20981                     this.errorEl.setWidth(elp.getWidth(true)-20);
20982                 }
20983                 this.errorEl.update(msg);
20984                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20985                 break;
20986             case 'side':
20987                 if(!this.errorIcon){
20988                     var elp = this.el.findParent('.x-form-element', 5, true);
20989                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20990                 }
20991                 this.alignErrorIcon();
20992                 this.errorIcon.dom.qtip = msg;
20993                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20994                 this.errorIcon.show();
20995                 this.on('resize', this.alignErrorIcon, this);
20996                 break;
20997             default:
20998                 var t = Roo.getDom(this.msgTarget);
20999                 t.innerHTML = msg;
21000                 t.style.display = this.msgDisplay;
21001                 break;
21002         }
21003         this.fireEvent('invalid', this, msg);
21004     },
21005
21006     // private
21007     alignErrorIcon : function(){
21008         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21009     },
21010
21011     /**
21012      * Clear any invalid styles/messages for this field
21013      */
21014     clearInvalid : function(){
21015         if(!this.rendered || this.preventMark){ // not rendered
21016             return;
21017         }
21018         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21019         
21020         obj.el.removeClass(this.invalidClass);
21021         switch(this.msgTarget){
21022             case 'qtip':
21023                 obj.el.dom.qtip = '';
21024                 break;
21025             case 'title':
21026                 this.el.dom.title = '';
21027                 break;
21028             case 'under':
21029                 if(this.errorEl){
21030                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21031                 }
21032                 break;
21033             case 'side':
21034                 if(this.errorIcon){
21035                     this.errorIcon.dom.qtip = '';
21036                     this.errorIcon.hide();
21037                     this.un('resize', this.alignErrorIcon, this);
21038                 }
21039                 break;
21040             default:
21041                 var t = Roo.getDom(this.msgTarget);
21042                 t.innerHTML = '';
21043                 t.style.display = 'none';
21044                 break;
21045         }
21046         this.fireEvent('valid', this);
21047     },
21048
21049     /**
21050      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21051      * @return {Mixed} value The field value
21052      */
21053     getRawValue : function(){
21054         var v = this.el.getValue();
21055         
21056         return v;
21057     },
21058
21059     /**
21060      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21061      * @return {Mixed} value The field value
21062      */
21063     getValue : function(){
21064         var v = this.el.getValue();
21065          
21066         return v;
21067     },
21068
21069     /**
21070      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21071      * @param {Mixed} value The value to set
21072      */
21073     setRawValue : function(v){
21074         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21075     },
21076
21077     /**
21078      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21079      * @param {Mixed} value The value to set
21080      */
21081     setValue : function(v){
21082         this.value = v;
21083         if(this.rendered){
21084             this.el.dom.value = (v === null || v === undefined ? '' : v);
21085              this.validate();
21086         }
21087     },
21088
21089     adjustSize : function(w, h){
21090         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21091         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21092         return s;
21093     },
21094
21095     adjustWidth : function(tag, w){
21096         tag = tag.toLowerCase();
21097         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21098             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21099                 if(tag == 'input'){
21100                     return w + 2;
21101                 }
21102                 if(tag == 'textarea'){
21103                     return w-2;
21104                 }
21105             }else if(Roo.isOpera){
21106                 if(tag == 'input'){
21107                     return w + 2;
21108                 }
21109                 if(tag == 'textarea'){
21110                     return w-2;
21111                 }
21112             }
21113         }
21114         return w;
21115     }
21116 });
21117
21118
21119 // anything other than normal should be considered experimental
21120 Roo.form.Field.msgFx = {
21121     normal : {
21122         show: function(msgEl, f){
21123             msgEl.setDisplayed('block');
21124         },
21125
21126         hide : function(msgEl, f){
21127             msgEl.setDisplayed(false).update('');
21128         }
21129     },
21130
21131     slide : {
21132         show: function(msgEl, f){
21133             msgEl.slideIn('t', {stopFx:true});
21134         },
21135
21136         hide : function(msgEl, f){
21137             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21138         }
21139     },
21140
21141     slideRight : {
21142         show: function(msgEl, f){
21143             msgEl.fixDisplay();
21144             msgEl.alignTo(f.el, 'tl-tr');
21145             msgEl.slideIn('l', {stopFx:true});
21146         },
21147
21148         hide : function(msgEl, f){
21149             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21150         }
21151     }
21152 };/*
21153  * Based on:
21154  * Ext JS Library 1.1.1
21155  * Copyright(c) 2006-2007, Ext JS, LLC.
21156  *
21157  * Originally Released Under LGPL - original licence link has changed is not relivant.
21158  *
21159  * Fork - LGPL
21160  * <script type="text/javascript">
21161  */
21162  
21163
21164 /**
21165  * @class Roo.form.TextField
21166  * @extends Roo.form.Field
21167  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21168  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21169  * @constructor
21170  * Creates a new TextField
21171  * @param {Object} config Configuration options
21172  */
21173 Roo.form.TextField = function(config){
21174     Roo.form.TextField.superclass.constructor.call(this, config);
21175     this.addEvents({
21176         /**
21177          * @event autosize
21178          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21179          * according to the default logic, but this event provides a hook for the developer to apply additional
21180          * logic at runtime to resize the field if needed.
21181              * @param {Roo.form.Field} this This text field
21182              * @param {Number} width The new field width
21183              */
21184         autosize : true
21185     });
21186 };
21187
21188 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21189     /**
21190      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21191      */
21192     grow : false,
21193     /**
21194      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21195      */
21196     growMin : 30,
21197     /**
21198      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21199      */
21200     growMax : 800,
21201     /**
21202      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21203      */
21204     vtype : null,
21205     /**
21206      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21207      */
21208     maskRe : null,
21209     /**
21210      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21211      */
21212     disableKeyFilter : false,
21213     /**
21214      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21215      */
21216     allowBlank : true,
21217     /**
21218      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21219      */
21220     minLength : 0,
21221     /**
21222      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21223      */
21224     maxLength : Number.MAX_VALUE,
21225     /**
21226      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21227      */
21228     minLengthText : "The minimum length for this field is {0}",
21229     /**
21230      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21231      */
21232     maxLengthText : "The maximum length for this field is {0}",
21233     /**
21234      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21235      */
21236     selectOnFocus : false,
21237     /**
21238      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21239      */
21240     blankText : "This field is required",
21241     /**
21242      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21243      * If available, this function will be called only after the basic validators all return true, and will be passed the
21244      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21245      */
21246     validator : null,
21247     /**
21248      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21249      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21250      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21251      */
21252     regex : null,
21253     /**
21254      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21255      */
21256     regexText : "",
21257     /**
21258      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21259      */
21260     emptyText : null,
21261    
21262
21263     // private
21264     initEvents : function()
21265     {
21266         if (this.emptyText) {
21267             this.el.attr('placeholder', this.emptyText);
21268         }
21269         
21270         Roo.form.TextField.superclass.initEvents.call(this);
21271         if(this.validationEvent == 'keyup'){
21272             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21273             this.el.on('keyup', this.filterValidation, this);
21274         }
21275         else if(this.validationEvent !== false){
21276             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21277         }
21278         
21279         if(this.selectOnFocus){
21280             this.on("focus", this.preFocus, this);
21281             
21282         }
21283         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21284             this.el.on("keypress", this.filterKeys, this);
21285         }
21286         if(this.grow){
21287             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21288             this.el.on("click", this.autoSize,  this);
21289         }
21290         if(this.el.is('input[type=password]') && Roo.isSafari){
21291             this.el.on('keydown', this.SafariOnKeyDown, this);
21292         }
21293     },
21294
21295     processValue : function(value){
21296         if(this.stripCharsRe){
21297             var newValue = value.replace(this.stripCharsRe, '');
21298             if(newValue !== value){
21299                 this.setRawValue(newValue);
21300                 return newValue;
21301             }
21302         }
21303         return value;
21304     },
21305
21306     filterValidation : function(e){
21307         if(!e.isNavKeyPress()){
21308             this.validationTask.delay(this.validationDelay);
21309         }
21310     },
21311
21312     // private
21313     onKeyUp : function(e){
21314         if(!e.isNavKeyPress()){
21315             this.autoSize();
21316         }
21317     },
21318
21319     /**
21320      * Resets the current field value to the originally-loaded value and clears any validation messages.
21321      *  
21322      */
21323     reset : function(){
21324         Roo.form.TextField.superclass.reset.call(this);
21325        
21326     },
21327
21328     
21329     // private
21330     preFocus : function(){
21331         
21332         if(this.selectOnFocus){
21333             this.el.dom.select();
21334         }
21335     },
21336
21337     
21338     // private
21339     filterKeys : function(e){
21340         var k = e.getKey();
21341         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21342             return;
21343         }
21344         var c = e.getCharCode(), cc = String.fromCharCode(c);
21345         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21346             return;
21347         }
21348         if(!this.maskRe.test(cc)){
21349             e.stopEvent();
21350         }
21351     },
21352
21353     setValue : function(v){
21354         
21355         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21356         
21357         this.autoSize();
21358     },
21359
21360     /**
21361      * Validates a value according to the field's validation rules and marks the field as invalid
21362      * if the validation fails
21363      * @param {Mixed} value The value to validate
21364      * @return {Boolean} True if the value is valid, else false
21365      */
21366     validateValue : function(value){
21367         if(value.length < 1)  { // if it's blank
21368              if(this.allowBlank){
21369                 this.clearInvalid();
21370                 return true;
21371              }else{
21372                 this.markInvalid(this.blankText);
21373                 return false;
21374              }
21375         }
21376         if(value.length < this.minLength){
21377             this.markInvalid(String.format(this.minLengthText, this.minLength));
21378             return false;
21379         }
21380         if(value.length > this.maxLength){
21381             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21382             return false;
21383         }
21384         if(this.vtype){
21385             var vt = Roo.form.VTypes;
21386             if(!vt[this.vtype](value, this)){
21387                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21388                 return false;
21389             }
21390         }
21391         if(typeof this.validator == "function"){
21392             var msg = this.validator(value);
21393             if(msg !== true){
21394                 this.markInvalid(msg);
21395                 return false;
21396             }
21397         }
21398         if(this.regex && !this.regex.test(value)){
21399             this.markInvalid(this.regexText);
21400             return false;
21401         }
21402         return true;
21403     },
21404
21405     /**
21406      * Selects text in this field
21407      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21408      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21409      */
21410     selectText : function(start, end){
21411         var v = this.getRawValue();
21412         if(v.length > 0){
21413             start = start === undefined ? 0 : start;
21414             end = end === undefined ? v.length : end;
21415             var d = this.el.dom;
21416             if(d.setSelectionRange){
21417                 d.setSelectionRange(start, end);
21418             }else if(d.createTextRange){
21419                 var range = d.createTextRange();
21420                 range.moveStart("character", start);
21421                 range.moveEnd("character", v.length-end);
21422                 range.select();
21423             }
21424         }
21425     },
21426
21427     /**
21428      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21429      * This only takes effect if grow = true, and fires the autosize event.
21430      */
21431     autoSize : function(){
21432         if(!this.grow || !this.rendered){
21433             return;
21434         }
21435         if(!this.metrics){
21436             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21437         }
21438         var el = this.el;
21439         var v = el.dom.value;
21440         var d = document.createElement('div');
21441         d.appendChild(document.createTextNode(v));
21442         v = d.innerHTML;
21443         d = null;
21444         v += "&#160;";
21445         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21446         this.el.setWidth(w);
21447         this.fireEvent("autosize", this, w);
21448     },
21449     
21450     // private
21451     SafariOnKeyDown : function(event)
21452     {
21453         // this is a workaround for a password hang bug on chrome/ webkit.
21454         
21455         var isSelectAll = false;
21456         
21457         if(this.el.dom.selectionEnd > 0){
21458             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21459         }
21460         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21461             event.preventDefault();
21462             this.setValue('');
21463             return;
21464         }
21465         
21466         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21467             
21468             event.preventDefault();
21469             // this is very hacky as keydown always get's upper case.
21470             
21471             var cc = String.fromCharCode(event.getCharCode());
21472             
21473             
21474             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21475             
21476         }
21477         
21478         
21479     }
21480 });/*
21481  * Based on:
21482  * Ext JS Library 1.1.1
21483  * Copyright(c) 2006-2007, Ext JS, LLC.
21484  *
21485  * Originally Released Under LGPL - original licence link has changed is not relivant.
21486  *
21487  * Fork - LGPL
21488  * <script type="text/javascript">
21489  */
21490  
21491 /**
21492  * @class Roo.form.Hidden
21493  * @extends Roo.form.TextField
21494  * Simple Hidden element used on forms 
21495  * 
21496  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21497  * 
21498  * @constructor
21499  * Creates a new Hidden form element.
21500  * @param {Object} config Configuration options
21501  */
21502
21503
21504
21505 // easy hidden field...
21506 Roo.form.Hidden = function(config){
21507     Roo.form.Hidden.superclass.constructor.call(this, config);
21508 };
21509   
21510 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21511     fieldLabel:      '',
21512     inputType:      'hidden',
21513     width:          50,
21514     allowBlank:     true,
21515     labelSeparator: '',
21516     hidden:         true,
21517     itemCls :       'x-form-item-display-none'
21518
21519
21520 });
21521
21522
21523 /*
21524  * Based on:
21525  * Ext JS Library 1.1.1
21526  * Copyright(c) 2006-2007, Ext JS, LLC.
21527  *
21528  * Originally Released Under LGPL - original licence link has changed is not relivant.
21529  *
21530  * Fork - LGPL
21531  * <script type="text/javascript">
21532  */
21533  
21534 /**
21535  * @class Roo.form.TriggerField
21536  * @extends Roo.form.TextField
21537  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21538  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21539  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21540  * for which you can provide a custom implementation.  For example:
21541  * <pre><code>
21542 var trigger = new Roo.form.TriggerField();
21543 trigger.onTriggerClick = myTriggerFn;
21544 trigger.applyTo('my-field');
21545 </code></pre>
21546  *
21547  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21548  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21549  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21550  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21551  * @constructor
21552  * Create a new TriggerField.
21553  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21554  * to the base TextField)
21555  */
21556 Roo.form.TriggerField = function(config){
21557     this.mimicing = false;
21558     Roo.form.TriggerField.superclass.constructor.call(this, config);
21559 };
21560
21561 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21562     /**
21563      * @cfg {String} triggerClass A CSS class to apply to the trigger
21564      */
21565     /**
21566      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21567      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21568      */
21569     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21570     /**
21571      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21572      */
21573     hideTrigger:false,
21574
21575     /** @cfg {Boolean} grow @hide */
21576     /** @cfg {Number} growMin @hide */
21577     /** @cfg {Number} growMax @hide */
21578
21579     /**
21580      * @hide 
21581      * @method
21582      */
21583     autoSize: Roo.emptyFn,
21584     // private
21585     monitorTab : true,
21586     // private
21587     deferHeight : true,
21588
21589     
21590     actionMode : 'wrap',
21591     // private
21592     onResize : function(w, h){
21593         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21594         if(typeof w == 'number'){
21595             var x = w - this.trigger.getWidth();
21596             this.el.setWidth(this.adjustWidth('input', x));
21597             this.trigger.setStyle('left', x+'px');
21598         }
21599     },
21600
21601     // private
21602     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21603
21604     // private
21605     getResizeEl : function(){
21606         return this.wrap;
21607     },
21608
21609     // private
21610     getPositionEl : function(){
21611         return this.wrap;
21612     },
21613
21614     // private
21615     alignErrorIcon : function(){
21616         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21617     },
21618
21619     // private
21620     onRender : function(ct, position){
21621         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21622         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21623         this.trigger = this.wrap.createChild(this.triggerConfig ||
21624                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21625         if(this.hideTrigger){
21626             this.trigger.setDisplayed(false);
21627         }
21628         this.initTrigger();
21629         if(!this.width){
21630             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21631         }
21632     },
21633
21634     // private
21635     initTrigger : function(){
21636         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21637         this.trigger.addClassOnOver('x-form-trigger-over');
21638         this.trigger.addClassOnClick('x-form-trigger-click');
21639     },
21640
21641     // private
21642     onDestroy : function(){
21643         if(this.trigger){
21644             this.trigger.removeAllListeners();
21645             this.trigger.remove();
21646         }
21647         if(this.wrap){
21648             this.wrap.remove();
21649         }
21650         Roo.form.TriggerField.superclass.onDestroy.call(this);
21651     },
21652
21653     // private
21654     onFocus : function(){
21655         Roo.form.TriggerField.superclass.onFocus.call(this);
21656         if(!this.mimicing){
21657             this.wrap.addClass('x-trigger-wrap-focus');
21658             this.mimicing = true;
21659             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21660             if(this.monitorTab){
21661                 this.el.on("keydown", this.checkTab, this);
21662             }
21663         }
21664     },
21665
21666     // private
21667     checkTab : function(e){
21668         if(e.getKey() == e.TAB){
21669             this.triggerBlur();
21670         }
21671     },
21672
21673     // private
21674     onBlur : function(){
21675         // do nothing
21676     },
21677
21678     // private
21679     mimicBlur : function(e, t){
21680         if(!this.wrap.contains(t) && this.validateBlur()){
21681             this.triggerBlur();
21682         }
21683     },
21684
21685     // private
21686     triggerBlur : function(){
21687         this.mimicing = false;
21688         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21689         if(this.monitorTab){
21690             this.el.un("keydown", this.checkTab, this);
21691         }
21692         this.wrap.removeClass('x-trigger-wrap-focus');
21693         Roo.form.TriggerField.superclass.onBlur.call(this);
21694     },
21695
21696     // private
21697     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21698     validateBlur : function(e, t){
21699         return true;
21700     },
21701
21702     // private
21703     onDisable : function(){
21704         Roo.form.TriggerField.superclass.onDisable.call(this);
21705         if(this.wrap){
21706             this.wrap.addClass('x-item-disabled');
21707         }
21708     },
21709
21710     // private
21711     onEnable : function(){
21712         Roo.form.TriggerField.superclass.onEnable.call(this);
21713         if(this.wrap){
21714             this.wrap.removeClass('x-item-disabled');
21715         }
21716     },
21717
21718     // private
21719     onShow : function(){
21720         var ae = this.getActionEl();
21721         
21722         if(ae){
21723             ae.dom.style.display = '';
21724             ae.dom.style.visibility = 'visible';
21725         }
21726     },
21727
21728     // private
21729     
21730     onHide : function(){
21731         var ae = this.getActionEl();
21732         ae.dom.style.display = 'none';
21733     },
21734
21735     /**
21736      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21737      * by an implementing function.
21738      * @method
21739      * @param {EventObject} e
21740      */
21741     onTriggerClick : Roo.emptyFn
21742 });
21743
21744 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21745 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21746 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21747 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21748     initComponent : function(){
21749         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21750
21751         this.triggerConfig = {
21752             tag:'span', cls:'x-form-twin-triggers', cn:[
21753             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21754             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21755         ]};
21756     },
21757
21758     getTrigger : function(index){
21759         return this.triggers[index];
21760     },
21761
21762     initTrigger : function(){
21763         var ts = this.trigger.select('.x-form-trigger', true);
21764         this.wrap.setStyle('overflow', 'hidden');
21765         var triggerField = this;
21766         ts.each(function(t, all, index){
21767             t.hide = function(){
21768                 var w = triggerField.wrap.getWidth();
21769                 this.dom.style.display = 'none';
21770                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21771             };
21772             t.show = function(){
21773                 var w = triggerField.wrap.getWidth();
21774                 this.dom.style.display = '';
21775                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21776             };
21777             var triggerIndex = 'Trigger'+(index+1);
21778
21779             if(this['hide'+triggerIndex]){
21780                 t.dom.style.display = 'none';
21781             }
21782             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21783             t.addClassOnOver('x-form-trigger-over');
21784             t.addClassOnClick('x-form-trigger-click');
21785         }, this);
21786         this.triggers = ts.elements;
21787     },
21788
21789     onTrigger1Click : Roo.emptyFn,
21790     onTrigger2Click : Roo.emptyFn
21791 });/*
21792  * Based on:
21793  * Ext JS Library 1.1.1
21794  * Copyright(c) 2006-2007, Ext JS, LLC.
21795  *
21796  * Originally Released Under LGPL - original licence link has changed is not relivant.
21797  *
21798  * Fork - LGPL
21799  * <script type="text/javascript">
21800  */
21801  
21802 /**
21803  * @class Roo.form.TextArea
21804  * @extends Roo.form.TextField
21805  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21806  * support for auto-sizing.
21807  * @constructor
21808  * Creates a new TextArea
21809  * @param {Object} config Configuration options
21810  */
21811 Roo.form.TextArea = function(config){
21812     Roo.form.TextArea.superclass.constructor.call(this, config);
21813     // these are provided exchanges for backwards compat
21814     // minHeight/maxHeight were replaced by growMin/growMax to be
21815     // compatible with TextField growing config values
21816     if(this.minHeight !== undefined){
21817         this.growMin = this.minHeight;
21818     }
21819     if(this.maxHeight !== undefined){
21820         this.growMax = this.maxHeight;
21821     }
21822 };
21823
21824 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21825     /**
21826      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21827      */
21828     growMin : 60,
21829     /**
21830      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21831      */
21832     growMax: 1000,
21833     /**
21834      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21835      * in the field (equivalent to setting overflow: hidden, defaults to false)
21836      */
21837     preventScrollbars: false,
21838     /**
21839      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21840      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21841      */
21842
21843     // private
21844     onRender : function(ct, position){
21845         if(!this.el){
21846             this.defaultAutoCreate = {
21847                 tag: "textarea",
21848                 style:"width:300px;height:60px;",
21849                 autocomplete: "new-password"
21850             };
21851         }
21852         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21853         if(this.grow){
21854             this.textSizeEl = Roo.DomHelper.append(document.body, {
21855                 tag: "pre", cls: "x-form-grow-sizer"
21856             });
21857             if(this.preventScrollbars){
21858                 this.el.setStyle("overflow", "hidden");
21859             }
21860             this.el.setHeight(this.growMin);
21861         }
21862     },
21863
21864     onDestroy : function(){
21865         if(this.textSizeEl){
21866             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21867         }
21868         Roo.form.TextArea.superclass.onDestroy.call(this);
21869     },
21870
21871     // private
21872     onKeyUp : function(e){
21873         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21874             this.autoSize();
21875         }
21876     },
21877
21878     /**
21879      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21880      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21881      */
21882     autoSize : function(){
21883         if(!this.grow || !this.textSizeEl){
21884             return;
21885         }
21886         var el = this.el;
21887         var v = el.dom.value;
21888         var ts = this.textSizeEl;
21889
21890         ts.innerHTML = '';
21891         ts.appendChild(document.createTextNode(v));
21892         v = ts.innerHTML;
21893
21894         Roo.fly(ts).setWidth(this.el.getWidth());
21895         if(v.length < 1){
21896             v = "&#160;&#160;";
21897         }else{
21898             if(Roo.isIE){
21899                 v = v.replace(/\n/g, '<p>&#160;</p>');
21900             }
21901             v += "&#160;\n&#160;";
21902         }
21903         ts.innerHTML = v;
21904         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21905         if(h != this.lastHeight){
21906             this.lastHeight = h;
21907             this.el.setHeight(h);
21908             this.fireEvent("autosize", this, h);
21909         }
21910     }
21911 });/*
21912  * Based on:
21913  * Ext JS Library 1.1.1
21914  * Copyright(c) 2006-2007, Ext JS, LLC.
21915  *
21916  * Originally Released Under LGPL - original licence link has changed is not relivant.
21917  *
21918  * Fork - LGPL
21919  * <script type="text/javascript">
21920  */
21921  
21922
21923 /**
21924  * @class Roo.form.NumberField
21925  * @extends Roo.form.TextField
21926  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21927  * @constructor
21928  * Creates a new NumberField
21929  * @param {Object} config Configuration options
21930  */
21931 Roo.form.NumberField = function(config){
21932     Roo.form.NumberField.superclass.constructor.call(this, config);
21933 };
21934
21935 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21936     /**
21937      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21938      */
21939     fieldClass: "x-form-field x-form-num-field",
21940     /**
21941      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21942      */
21943     allowDecimals : true,
21944     /**
21945      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21946      */
21947     decimalSeparator : ".",
21948     /**
21949      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21950      */
21951     decimalPrecision : 2,
21952     /**
21953      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21954      */
21955     allowNegative : true,
21956     /**
21957      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21958      */
21959     minValue : Number.NEGATIVE_INFINITY,
21960     /**
21961      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21962      */
21963     maxValue : Number.MAX_VALUE,
21964     /**
21965      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21966      */
21967     minText : "The minimum value for this field is {0}",
21968     /**
21969      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21970      */
21971     maxText : "The maximum value for this field is {0}",
21972     /**
21973      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21974      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21975      */
21976     nanText : "{0} is not a valid number",
21977
21978     // private
21979     initEvents : function(){
21980         Roo.form.NumberField.superclass.initEvents.call(this);
21981         var allowed = "0123456789";
21982         if(this.allowDecimals){
21983             allowed += this.decimalSeparator;
21984         }
21985         if(this.allowNegative){
21986             allowed += "-";
21987         }
21988         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21989         var keyPress = function(e){
21990             var k = e.getKey();
21991             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21992                 return;
21993             }
21994             var c = e.getCharCode();
21995             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21996                 e.stopEvent();
21997             }
21998         };
21999         this.el.on("keypress", keyPress, this);
22000     },
22001
22002     // private
22003     validateValue : function(value){
22004         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22005             return false;
22006         }
22007         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22008              return true;
22009         }
22010         var num = this.parseValue(value);
22011         if(isNaN(num)){
22012             this.markInvalid(String.format(this.nanText, value));
22013             return false;
22014         }
22015         if(num < this.minValue){
22016             this.markInvalid(String.format(this.minText, this.minValue));
22017             return false;
22018         }
22019         if(num > this.maxValue){
22020             this.markInvalid(String.format(this.maxText, this.maxValue));
22021             return false;
22022         }
22023         return true;
22024     },
22025
22026     getValue : function(){
22027         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22028     },
22029
22030     // private
22031     parseValue : function(value){
22032         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22033         return isNaN(value) ? '' : value;
22034     },
22035
22036     // private
22037     fixPrecision : function(value){
22038         var nan = isNaN(value);
22039         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22040             return nan ? '' : value;
22041         }
22042         return parseFloat(value).toFixed(this.decimalPrecision);
22043     },
22044
22045     setValue : function(v){
22046         v = this.fixPrecision(v);
22047         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22048     },
22049
22050     // private
22051     decimalPrecisionFcn : function(v){
22052         return Math.floor(v);
22053     },
22054
22055     beforeBlur : function(){
22056         var v = this.parseValue(this.getRawValue());
22057         if(v){
22058             this.setValue(v);
22059         }
22060     }
22061 });/*
22062  * Based on:
22063  * Ext JS Library 1.1.1
22064  * Copyright(c) 2006-2007, Ext JS, LLC.
22065  *
22066  * Originally Released Under LGPL - original licence link has changed is not relivant.
22067  *
22068  * Fork - LGPL
22069  * <script type="text/javascript">
22070  */
22071  
22072 /**
22073  * @class Roo.form.DateField
22074  * @extends Roo.form.TriggerField
22075  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22076 * @constructor
22077 * Create a new DateField
22078 * @param {Object} config
22079  */
22080 Roo.form.DateField = function(config){
22081     Roo.form.DateField.superclass.constructor.call(this, config);
22082     
22083       this.addEvents({
22084          
22085         /**
22086          * @event select
22087          * Fires when a date is selected
22088              * @param {Roo.form.DateField} combo This combo box
22089              * @param {Date} date The date selected
22090              */
22091         'select' : true
22092          
22093     });
22094     
22095     
22096     if(typeof this.minValue == "string") {
22097         this.minValue = this.parseDate(this.minValue);
22098     }
22099     if(typeof this.maxValue == "string") {
22100         this.maxValue = this.parseDate(this.maxValue);
22101     }
22102     this.ddMatch = null;
22103     if(this.disabledDates){
22104         var dd = this.disabledDates;
22105         var re = "(?:";
22106         for(var i = 0; i < dd.length; i++){
22107             re += dd[i];
22108             if(i != dd.length-1) {
22109                 re += "|";
22110             }
22111         }
22112         this.ddMatch = new RegExp(re + ")");
22113     }
22114 };
22115
22116 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22117     /**
22118      * @cfg {String} format
22119      * The default date format string which can be overriden for localization support.  The format must be
22120      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22121      */
22122     format : "m/d/y",
22123     /**
22124      * @cfg {String} altFormats
22125      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22126      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22127      */
22128     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22129     /**
22130      * @cfg {Array} disabledDays
22131      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22132      */
22133     disabledDays : null,
22134     /**
22135      * @cfg {String} disabledDaysText
22136      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22137      */
22138     disabledDaysText : "Disabled",
22139     /**
22140      * @cfg {Array} disabledDates
22141      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22142      * expression so they are very powerful. Some examples:
22143      * <ul>
22144      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22145      * <li>["03/08", "09/16"] would disable those days for every year</li>
22146      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22147      * <li>["03/../2006"] would disable every day in March 2006</li>
22148      * <li>["^03"] would disable every day in every March</li>
22149      * </ul>
22150      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22151      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22152      */
22153     disabledDates : null,
22154     /**
22155      * @cfg {String} disabledDatesText
22156      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22157      */
22158     disabledDatesText : "Disabled",
22159     /**
22160      * @cfg {Date/String} minValue
22161      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22162      * valid format (defaults to null).
22163      */
22164     minValue : null,
22165     /**
22166      * @cfg {Date/String} maxValue
22167      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22168      * valid format (defaults to null).
22169      */
22170     maxValue : null,
22171     /**
22172      * @cfg {String} minText
22173      * The error text to display when the date in the cell is before minValue (defaults to
22174      * 'The date in this field must be after {minValue}').
22175      */
22176     minText : "The date in this field must be equal to or after {0}",
22177     /**
22178      * @cfg {String} maxText
22179      * The error text to display when the date in the cell is after maxValue (defaults to
22180      * 'The date in this field must be before {maxValue}').
22181      */
22182     maxText : "The date in this field must be equal to or before {0}",
22183     /**
22184      * @cfg {String} invalidText
22185      * The error text to display when the date in the field is invalid (defaults to
22186      * '{value} is not a valid date - it must be in the format {format}').
22187      */
22188     invalidText : "{0} is not a valid date - it must be in the format {1}",
22189     /**
22190      * @cfg {String} triggerClass
22191      * An additional CSS class used to style the trigger button.  The trigger will always get the
22192      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22193      * which displays a calendar icon).
22194      */
22195     triggerClass : 'x-form-date-trigger',
22196     
22197
22198     /**
22199      * @cfg {Boolean} useIso
22200      * if enabled, then the date field will use a hidden field to store the 
22201      * real value as iso formated date. default (false)
22202      */ 
22203     useIso : false,
22204     /**
22205      * @cfg {String/Object} autoCreate
22206      * A DomHelper element spec, or true for a default element spec (defaults to
22207      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22208      */ 
22209     // private
22210     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22211     
22212     // private
22213     hiddenField: false,
22214     
22215     onRender : function(ct, position)
22216     {
22217         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22218         if (this.useIso) {
22219             //this.el.dom.removeAttribute('name'); 
22220             Roo.log("Changing name?");
22221             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22222             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22223                     'before', true);
22224             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22225             // prevent input submission
22226             this.hiddenName = this.name;
22227         }
22228             
22229             
22230     },
22231     
22232     // private
22233     validateValue : function(value)
22234     {
22235         value = this.formatDate(value);
22236         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22237             Roo.log('super failed');
22238             return false;
22239         }
22240         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22241              return true;
22242         }
22243         var svalue = value;
22244         value = this.parseDate(value);
22245         if(!value){
22246             Roo.log('parse date failed' + svalue);
22247             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22248             return false;
22249         }
22250         var time = value.getTime();
22251         if(this.minValue && time < this.minValue.getTime()){
22252             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22253             return false;
22254         }
22255         if(this.maxValue && time > this.maxValue.getTime()){
22256             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22257             return false;
22258         }
22259         if(this.disabledDays){
22260             var day = value.getDay();
22261             for(var i = 0; i < this.disabledDays.length; i++) {
22262                 if(day === this.disabledDays[i]){
22263                     this.markInvalid(this.disabledDaysText);
22264                     return false;
22265                 }
22266             }
22267         }
22268         var fvalue = this.formatDate(value);
22269         if(this.ddMatch && this.ddMatch.test(fvalue)){
22270             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22271             return false;
22272         }
22273         return true;
22274     },
22275
22276     // private
22277     // Provides logic to override the default TriggerField.validateBlur which just returns true
22278     validateBlur : function(){
22279         return !this.menu || !this.menu.isVisible();
22280     },
22281     
22282     getName: function()
22283     {
22284         // returns hidden if it's set..
22285         if (!this.rendered) {return ''};
22286         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22287         
22288     },
22289
22290     /**
22291      * Returns the current date value of the date field.
22292      * @return {Date} The date value
22293      */
22294     getValue : function(){
22295         
22296         return  this.hiddenField ?
22297                 this.hiddenField.value :
22298                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22299     },
22300
22301     /**
22302      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22303      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22304      * (the default format used is "m/d/y").
22305      * <br />Usage:
22306      * <pre><code>
22307 //All of these calls set the same date value (May 4, 2006)
22308
22309 //Pass a date object:
22310 var dt = new Date('5/4/06');
22311 dateField.setValue(dt);
22312
22313 //Pass a date string (default format):
22314 dateField.setValue('5/4/06');
22315
22316 //Pass a date string (custom format):
22317 dateField.format = 'Y-m-d';
22318 dateField.setValue('2006-5-4');
22319 </code></pre>
22320      * @param {String/Date} date The date or valid date string
22321      */
22322     setValue : function(date){
22323         if (this.hiddenField) {
22324             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22325         }
22326         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22327         // make sure the value field is always stored as a date..
22328         this.value = this.parseDate(date);
22329         
22330         
22331     },
22332
22333     // private
22334     parseDate : function(value){
22335         if(!value || value instanceof Date){
22336             return value;
22337         }
22338         var v = Date.parseDate(value, this.format);
22339          if (!v && this.useIso) {
22340             v = Date.parseDate(value, 'Y-m-d');
22341         }
22342         if(!v && this.altFormats){
22343             if(!this.altFormatsArray){
22344                 this.altFormatsArray = this.altFormats.split("|");
22345             }
22346             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22347                 v = Date.parseDate(value, this.altFormatsArray[i]);
22348             }
22349         }
22350         return v;
22351     },
22352
22353     // private
22354     formatDate : function(date, fmt){
22355         return (!date || !(date instanceof Date)) ?
22356                date : date.dateFormat(fmt || this.format);
22357     },
22358
22359     // private
22360     menuListeners : {
22361         select: function(m, d){
22362             
22363             this.setValue(d);
22364             this.fireEvent('select', this, d);
22365         },
22366         show : function(){ // retain focus styling
22367             this.onFocus();
22368         },
22369         hide : function(){
22370             this.focus.defer(10, this);
22371             var ml = this.menuListeners;
22372             this.menu.un("select", ml.select,  this);
22373             this.menu.un("show", ml.show,  this);
22374             this.menu.un("hide", ml.hide,  this);
22375         }
22376     },
22377
22378     // private
22379     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22380     onTriggerClick : function(){
22381         if(this.disabled){
22382             return;
22383         }
22384         if(this.menu == null){
22385             this.menu = new Roo.menu.DateMenu();
22386         }
22387         Roo.apply(this.menu.picker,  {
22388             showClear: this.allowBlank,
22389             minDate : this.minValue,
22390             maxDate : this.maxValue,
22391             disabledDatesRE : this.ddMatch,
22392             disabledDatesText : this.disabledDatesText,
22393             disabledDays : this.disabledDays,
22394             disabledDaysText : this.disabledDaysText,
22395             format : this.useIso ? 'Y-m-d' : this.format,
22396             minText : String.format(this.minText, this.formatDate(this.minValue)),
22397             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22398         });
22399         this.menu.on(Roo.apply({}, this.menuListeners, {
22400             scope:this
22401         }));
22402         this.menu.picker.setValue(this.getValue() || new Date());
22403         this.menu.show(this.el, "tl-bl?");
22404     },
22405
22406     beforeBlur : function(){
22407         var v = this.parseDate(this.getRawValue());
22408         if(v){
22409             this.setValue(v);
22410         }
22411     },
22412
22413     /*@
22414      * overide
22415      * 
22416      */
22417     isDirty : function() {
22418         if(this.disabled) {
22419             return false;
22420         }
22421         
22422         if(typeof(this.startValue) === 'undefined'){
22423             return false;
22424         }
22425         
22426         return String(this.getValue()) !== String(this.startValue);
22427         
22428     }
22429 });/*
22430  * Based on:
22431  * Ext JS Library 1.1.1
22432  * Copyright(c) 2006-2007, Ext JS, LLC.
22433  *
22434  * Originally Released Under LGPL - original licence link has changed is not relivant.
22435  *
22436  * Fork - LGPL
22437  * <script type="text/javascript">
22438  */
22439  
22440 /**
22441  * @class Roo.form.MonthField
22442  * @extends Roo.form.TriggerField
22443  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22444 * @constructor
22445 * Create a new MonthField
22446 * @param {Object} config
22447  */
22448 Roo.form.MonthField = function(config){
22449     
22450     Roo.form.MonthField.superclass.constructor.call(this, config);
22451     
22452       this.addEvents({
22453          
22454         /**
22455          * @event select
22456          * Fires when a date is selected
22457              * @param {Roo.form.MonthFieeld} combo This combo box
22458              * @param {Date} date The date selected
22459              */
22460         'select' : true
22461          
22462     });
22463     
22464     
22465     if(typeof this.minValue == "string") {
22466         this.minValue = this.parseDate(this.minValue);
22467     }
22468     if(typeof this.maxValue == "string") {
22469         this.maxValue = this.parseDate(this.maxValue);
22470     }
22471     this.ddMatch = null;
22472     if(this.disabledDates){
22473         var dd = this.disabledDates;
22474         var re = "(?:";
22475         for(var i = 0; i < dd.length; i++){
22476             re += dd[i];
22477             if(i != dd.length-1) {
22478                 re += "|";
22479             }
22480         }
22481         this.ddMatch = new RegExp(re + ")");
22482     }
22483 };
22484
22485 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22486     /**
22487      * @cfg {String} format
22488      * The default date format string which can be overriden for localization support.  The format must be
22489      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22490      */
22491     format : "M Y",
22492     /**
22493      * @cfg {String} altFormats
22494      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22495      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22496      */
22497     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22498     /**
22499      * @cfg {Array} disabledDays
22500      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22501      */
22502     disabledDays : [0,1,2,3,4,5,6],
22503     /**
22504      * @cfg {String} disabledDaysText
22505      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22506      */
22507     disabledDaysText : "Disabled",
22508     /**
22509      * @cfg {Array} disabledDates
22510      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22511      * expression so they are very powerful. Some examples:
22512      * <ul>
22513      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22514      * <li>["03/08", "09/16"] would disable those days for every year</li>
22515      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22516      * <li>["03/../2006"] would disable every day in March 2006</li>
22517      * <li>["^03"] would disable every day in every March</li>
22518      * </ul>
22519      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22520      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22521      */
22522     disabledDates : null,
22523     /**
22524      * @cfg {String} disabledDatesText
22525      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22526      */
22527     disabledDatesText : "Disabled",
22528     /**
22529      * @cfg {Date/String} minValue
22530      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22531      * valid format (defaults to null).
22532      */
22533     minValue : null,
22534     /**
22535      * @cfg {Date/String} maxValue
22536      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22537      * valid format (defaults to null).
22538      */
22539     maxValue : null,
22540     /**
22541      * @cfg {String} minText
22542      * The error text to display when the date in the cell is before minValue (defaults to
22543      * 'The date in this field must be after {minValue}').
22544      */
22545     minText : "The date in this field must be equal to or after {0}",
22546     /**
22547      * @cfg {String} maxTextf
22548      * The error text to display when the date in the cell is after maxValue (defaults to
22549      * 'The date in this field must be before {maxValue}').
22550      */
22551     maxText : "The date in this field must be equal to or before {0}",
22552     /**
22553      * @cfg {String} invalidText
22554      * The error text to display when the date in the field is invalid (defaults to
22555      * '{value} is not a valid date - it must be in the format {format}').
22556      */
22557     invalidText : "{0} is not a valid date - it must be in the format {1}",
22558     /**
22559      * @cfg {String} triggerClass
22560      * An additional CSS class used to style the trigger button.  The trigger will always get the
22561      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22562      * which displays a calendar icon).
22563      */
22564     triggerClass : 'x-form-date-trigger',
22565     
22566
22567     /**
22568      * @cfg {Boolean} useIso
22569      * if enabled, then the date field will use a hidden field to store the 
22570      * real value as iso formated date. default (true)
22571      */ 
22572     useIso : true,
22573     /**
22574      * @cfg {String/Object} autoCreate
22575      * A DomHelper element spec, or true for a default element spec (defaults to
22576      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22577      */ 
22578     // private
22579     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22580     
22581     // private
22582     hiddenField: false,
22583     
22584     hideMonthPicker : false,
22585     
22586     onRender : function(ct, position)
22587     {
22588         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22589         if (this.useIso) {
22590             this.el.dom.removeAttribute('name'); 
22591             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22592                     'before', true);
22593             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22594             // prevent input submission
22595             this.hiddenName = this.name;
22596         }
22597             
22598             
22599     },
22600     
22601     // private
22602     validateValue : function(value)
22603     {
22604         value = this.formatDate(value);
22605         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22606             return false;
22607         }
22608         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22609              return true;
22610         }
22611         var svalue = value;
22612         value = this.parseDate(value);
22613         if(!value){
22614             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22615             return false;
22616         }
22617         var time = value.getTime();
22618         if(this.minValue && time < this.minValue.getTime()){
22619             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22620             return false;
22621         }
22622         if(this.maxValue && time > this.maxValue.getTime()){
22623             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22624             return false;
22625         }
22626         /*if(this.disabledDays){
22627             var day = value.getDay();
22628             for(var i = 0; i < this.disabledDays.length; i++) {
22629                 if(day === this.disabledDays[i]){
22630                     this.markInvalid(this.disabledDaysText);
22631                     return false;
22632                 }
22633             }
22634         }
22635         */
22636         var fvalue = this.formatDate(value);
22637         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22638             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22639             return false;
22640         }
22641         */
22642         return true;
22643     },
22644
22645     // private
22646     // Provides logic to override the default TriggerField.validateBlur which just returns true
22647     validateBlur : function(){
22648         return !this.menu || !this.menu.isVisible();
22649     },
22650
22651     /**
22652      * Returns the current date value of the date field.
22653      * @return {Date} The date value
22654      */
22655     getValue : function(){
22656         
22657         
22658         
22659         return  this.hiddenField ?
22660                 this.hiddenField.value :
22661                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22662     },
22663
22664     /**
22665      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22666      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22667      * (the default format used is "m/d/y").
22668      * <br />Usage:
22669      * <pre><code>
22670 //All of these calls set the same date value (May 4, 2006)
22671
22672 //Pass a date object:
22673 var dt = new Date('5/4/06');
22674 monthField.setValue(dt);
22675
22676 //Pass a date string (default format):
22677 monthField.setValue('5/4/06');
22678
22679 //Pass a date string (custom format):
22680 monthField.format = 'Y-m-d';
22681 monthField.setValue('2006-5-4');
22682 </code></pre>
22683      * @param {String/Date} date The date or valid date string
22684      */
22685     setValue : function(date){
22686         Roo.log('month setValue' + date);
22687         // can only be first of month..
22688         
22689         var val = this.parseDate(date);
22690         
22691         if (this.hiddenField) {
22692             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22693         }
22694         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22695         this.value = this.parseDate(date);
22696     },
22697
22698     // private
22699     parseDate : function(value){
22700         if(!value || value instanceof Date){
22701             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22702             return value;
22703         }
22704         var v = Date.parseDate(value, this.format);
22705         if (!v && this.useIso) {
22706             v = Date.parseDate(value, 'Y-m-d');
22707         }
22708         if (v) {
22709             // 
22710             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22711         }
22712         
22713         
22714         if(!v && this.altFormats){
22715             if(!this.altFormatsArray){
22716                 this.altFormatsArray = this.altFormats.split("|");
22717             }
22718             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22719                 v = Date.parseDate(value, this.altFormatsArray[i]);
22720             }
22721         }
22722         return v;
22723     },
22724
22725     // private
22726     formatDate : function(date, fmt){
22727         return (!date || !(date instanceof Date)) ?
22728                date : date.dateFormat(fmt || this.format);
22729     },
22730
22731     // private
22732     menuListeners : {
22733         select: function(m, d){
22734             this.setValue(d);
22735             this.fireEvent('select', this, d);
22736         },
22737         show : function(){ // retain focus styling
22738             this.onFocus();
22739         },
22740         hide : function(){
22741             this.focus.defer(10, this);
22742             var ml = this.menuListeners;
22743             this.menu.un("select", ml.select,  this);
22744             this.menu.un("show", ml.show,  this);
22745             this.menu.un("hide", ml.hide,  this);
22746         }
22747     },
22748     // private
22749     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22750     onTriggerClick : function(){
22751         if(this.disabled){
22752             return;
22753         }
22754         if(this.menu == null){
22755             this.menu = new Roo.menu.DateMenu();
22756            
22757         }
22758         
22759         Roo.apply(this.menu.picker,  {
22760             
22761             showClear: this.allowBlank,
22762             minDate : this.minValue,
22763             maxDate : this.maxValue,
22764             disabledDatesRE : this.ddMatch,
22765             disabledDatesText : this.disabledDatesText,
22766             
22767             format : this.useIso ? 'Y-m-d' : this.format,
22768             minText : String.format(this.minText, this.formatDate(this.minValue)),
22769             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22770             
22771         });
22772          this.menu.on(Roo.apply({}, this.menuListeners, {
22773             scope:this
22774         }));
22775        
22776         
22777         var m = this.menu;
22778         var p = m.picker;
22779         
22780         // hide month picker get's called when we called by 'before hide';
22781         
22782         var ignorehide = true;
22783         p.hideMonthPicker  = function(disableAnim){
22784             if (ignorehide) {
22785                 return;
22786             }
22787              if(this.monthPicker){
22788                 Roo.log("hideMonthPicker called");
22789                 if(disableAnim === true){
22790                     this.monthPicker.hide();
22791                 }else{
22792                     this.monthPicker.slideOut('t', {duration:.2});
22793                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22794                     p.fireEvent("select", this, this.value);
22795                     m.hide();
22796                 }
22797             }
22798         }
22799         
22800         Roo.log('picker set value');
22801         Roo.log(this.getValue());
22802         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22803         m.show(this.el, 'tl-bl?');
22804         ignorehide  = false;
22805         // this will trigger hideMonthPicker..
22806         
22807         
22808         // hidden the day picker
22809         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22810         
22811         
22812         
22813       
22814         
22815         p.showMonthPicker.defer(100, p);
22816     
22817         
22818        
22819     },
22820
22821     beforeBlur : function(){
22822         var v = this.parseDate(this.getRawValue());
22823         if(v){
22824             this.setValue(v);
22825         }
22826     }
22827
22828     /** @cfg {Boolean} grow @hide */
22829     /** @cfg {Number} growMin @hide */
22830     /** @cfg {Number} growMax @hide */
22831     /**
22832      * @hide
22833      * @method autoSize
22834      */
22835 });/*
22836  * Based on:
22837  * Ext JS Library 1.1.1
22838  * Copyright(c) 2006-2007, Ext JS, LLC.
22839  *
22840  * Originally Released Under LGPL - original licence link has changed is not relivant.
22841  *
22842  * Fork - LGPL
22843  * <script type="text/javascript">
22844  */
22845  
22846
22847 /**
22848  * @class Roo.form.ComboBox
22849  * @extends Roo.form.TriggerField
22850  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22851  * @constructor
22852  * Create a new ComboBox.
22853  * @param {Object} config Configuration options
22854  */
22855 Roo.form.ComboBox = function(config){
22856     Roo.form.ComboBox.superclass.constructor.call(this, config);
22857     this.addEvents({
22858         /**
22859          * @event expand
22860          * Fires when the dropdown list is expanded
22861              * @param {Roo.form.ComboBox} combo This combo box
22862              */
22863         'expand' : true,
22864         /**
22865          * @event collapse
22866          * Fires when the dropdown list is collapsed
22867              * @param {Roo.form.ComboBox} combo This combo box
22868              */
22869         'collapse' : true,
22870         /**
22871          * @event beforeselect
22872          * Fires before a list item is selected. Return false to cancel the selection.
22873              * @param {Roo.form.ComboBox} combo This combo box
22874              * @param {Roo.data.Record} record The data record returned from the underlying store
22875              * @param {Number} index The index of the selected item in the dropdown list
22876              */
22877         'beforeselect' : true,
22878         /**
22879          * @event select
22880          * Fires when a list item is selected
22881              * @param {Roo.form.ComboBox} combo This combo box
22882              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22883              * @param {Number} index The index of the selected item in the dropdown list
22884              */
22885         'select' : true,
22886         /**
22887          * @event beforequery
22888          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22889          * The event object passed has these properties:
22890              * @param {Roo.form.ComboBox} combo This combo box
22891              * @param {String} query The query
22892              * @param {Boolean} forceAll true to force "all" query
22893              * @param {Boolean} cancel true to cancel the query
22894              * @param {Object} e The query event object
22895              */
22896         'beforequery': true,
22897          /**
22898          * @event add
22899          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22900              * @param {Roo.form.ComboBox} combo This combo box
22901              */
22902         'add' : true,
22903         /**
22904          * @event edit
22905          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22906              * @param {Roo.form.ComboBox} combo This combo box
22907              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22908              */
22909         'edit' : true
22910         
22911         
22912     });
22913     if(this.transform){
22914         this.allowDomMove = false;
22915         var s = Roo.getDom(this.transform);
22916         if(!this.hiddenName){
22917             this.hiddenName = s.name;
22918         }
22919         if(!this.store){
22920             this.mode = 'local';
22921             var d = [], opts = s.options;
22922             for(var i = 0, len = opts.length;i < len; i++){
22923                 var o = opts[i];
22924                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22925                 if(o.selected) {
22926                     this.value = value;
22927                 }
22928                 d.push([value, o.text]);
22929             }
22930             this.store = new Roo.data.SimpleStore({
22931                 'id': 0,
22932                 fields: ['value', 'text'],
22933                 data : d
22934             });
22935             this.valueField = 'value';
22936             this.displayField = 'text';
22937         }
22938         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22939         if(!this.lazyRender){
22940             this.target = true;
22941             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22942             s.parentNode.removeChild(s); // remove it
22943             this.render(this.el.parentNode);
22944         }else{
22945             s.parentNode.removeChild(s); // remove it
22946         }
22947
22948     }
22949     if (this.store) {
22950         this.store = Roo.factory(this.store, Roo.data);
22951     }
22952     
22953     this.selectedIndex = -1;
22954     if(this.mode == 'local'){
22955         if(config.queryDelay === undefined){
22956             this.queryDelay = 10;
22957         }
22958         if(config.minChars === undefined){
22959             this.minChars = 0;
22960         }
22961     }
22962 };
22963
22964 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22965     /**
22966      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22967      */
22968     /**
22969      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22970      * rendering into an Roo.Editor, defaults to false)
22971      */
22972     /**
22973      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22974      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22975      */
22976     /**
22977      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22978      */
22979     /**
22980      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22981      * the dropdown list (defaults to undefined, with no header element)
22982      */
22983
22984      /**
22985      * @cfg {String/Roo.Template} tpl The template to use to render the output
22986      */
22987      
22988     // private
22989     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22990     /**
22991      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22992      */
22993     listWidth: undefined,
22994     /**
22995      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22996      * mode = 'remote' or 'text' if mode = 'local')
22997      */
22998     displayField: undefined,
22999     /**
23000      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23001      * mode = 'remote' or 'value' if mode = 'local'). 
23002      * Note: use of a valueField requires the user make a selection
23003      * in order for a value to be mapped.
23004      */
23005     valueField: undefined,
23006     
23007     
23008     /**
23009      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23010      * field's data value (defaults to the underlying DOM element's name)
23011      */
23012     hiddenName: undefined,
23013     /**
23014      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23015      */
23016     listClass: '',
23017     /**
23018      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23019      */
23020     selectedClass: 'x-combo-selected',
23021     /**
23022      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23023      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23024      * which displays a downward arrow icon).
23025      */
23026     triggerClass : 'x-form-arrow-trigger',
23027     /**
23028      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23029      */
23030     shadow:'sides',
23031     /**
23032      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23033      * anchor positions (defaults to 'tl-bl')
23034      */
23035     listAlign: 'tl-bl?',
23036     /**
23037      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23038      */
23039     maxHeight: 300,
23040     /**
23041      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23042      * query specified by the allQuery config option (defaults to 'query')
23043      */
23044     triggerAction: 'query',
23045     /**
23046      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23047      * (defaults to 4, does not apply if editable = false)
23048      */
23049     minChars : 4,
23050     /**
23051      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23052      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23053      */
23054     typeAhead: false,
23055     /**
23056      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23057      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23058      */
23059     queryDelay: 500,
23060     /**
23061      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23062      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23063      */
23064     pageSize: 0,
23065     /**
23066      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23067      * when editable = true (defaults to false)
23068      */
23069     selectOnFocus:false,
23070     /**
23071      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23072      */
23073     queryParam: 'query',
23074     /**
23075      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23076      * when mode = 'remote' (defaults to 'Loading...')
23077      */
23078     loadingText: 'Loading...',
23079     /**
23080      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23081      */
23082     resizable: false,
23083     /**
23084      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23085      */
23086     handleHeight : 8,
23087     /**
23088      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23089      * traditional select (defaults to true)
23090      */
23091     editable: true,
23092     /**
23093      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23094      */
23095     allQuery: '',
23096     /**
23097      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23098      */
23099     mode: 'remote',
23100     /**
23101      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23102      * listWidth has a higher value)
23103      */
23104     minListWidth : 70,
23105     /**
23106      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23107      * allow the user to set arbitrary text into the field (defaults to false)
23108      */
23109     forceSelection:false,
23110     /**
23111      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23112      * if typeAhead = true (defaults to 250)
23113      */
23114     typeAheadDelay : 250,
23115     /**
23116      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23117      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23118      */
23119     valueNotFoundText : undefined,
23120     /**
23121      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23122      */
23123     blockFocus : false,
23124     
23125     /**
23126      * @cfg {Boolean} disableClear Disable showing of clear button.
23127      */
23128     disableClear : false,
23129     /**
23130      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23131      */
23132     alwaysQuery : false,
23133     
23134     //private
23135     addicon : false,
23136     editicon: false,
23137     
23138     // element that contains real text value.. (when hidden is used..)
23139      
23140     // private
23141     onRender : function(ct, position){
23142         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23143         if(this.hiddenName){
23144             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23145                     'before', true);
23146             this.hiddenField.value =
23147                 this.hiddenValue !== undefined ? this.hiddenValue :
23148                 this.value !== undefined ? this.value : '';
23149
23150             // prevent input submission
23151             this.el.dom.removeAttribute('name');
23152              
23153              
23154         }
23155         if(Roo.isGecko){
23156             this.el.dom.setAttribute('autocomplete', 'off');
23157         }
23158
23159         var cls = 'x-combo-list';
23160
23161         this.list = new Roo.Layer({
23162             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23163         });
23164
23165         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23166         this.list.setWidth(lw);
23167         this.list.swallowEvent('mousewheel');
23168         this.assetHeight = 0;
23169
23170         if(this.title){
23171             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23172             this.assetHeight += this.header.getHeight();
23173         }
23174
23175         this.innerList = this.list.createChild({cls:cls+'-inner'});
23176         this.innerList.on('mouseover', this.onViewOver, this);
23177         this.innerList.on('mousemove', this.onViewMove, this);
23178         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23179         
23180         if(this.allowBlank && !this.pageSize && !this.disableClear){
23181             this.footer = this.list.createChild({cls:cls+'-ft'});
23182             this.pageTb = new Roo.Toolbar(this.footer);
23183            
23184         }
23185         if(this.pageSize){
23186             this.footer = this.list.createChild({cls:cls+'-ft'});
23187             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23188                     {pageSize: this.pageSize});
23189             
23190         }
23191         
23192         if (this.pageTb && this.allowBlank && !this.disableClear) {
23193             var _this = this;
23194             this.pageTb.add(new Roo.Toolbar.Fill(), {
23195                 cls: 'x-btn-icon x-btn-clear',
23196                 text: '&#160;',
23197                 handler: function()
23198                 {
23199                     _this.collapse();
23200                     _this.clearValue();
23201                     _this.onSelect(false, -1);
23202                 }
23203             });
23204         }
23205         if (this.footer) {
23206             this.assetHeight += this.footer.getHeight();
23207         }
23208         
23209
23210         if(!this.tpl){
23211             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23212         }
23213
23214         this.view = new Roo.View(this.innerList, this.tpl, {
23215             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23216         });
23217
23218         this.view.on('click', this.onViewClick, this);
23219
23220         this.store.on('beforeload', this.onBeforeLoad, this);
23221         this.store.on('load', this.onLoad, this);
23222         this.store.on('loadexception', this.onLoadException, this);
23223
23224         if(this.resizable){
23225             this.resizer = new Roo.Resizable(this.list,  {
23226                pinned:true, handles:'se'
23227             });
23228             this.resizer.on('resize', function(r, w, h){
23229                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23230                 this.listWidth = w;
23231                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23232                 this.restrictHeight();
23233             }, this);
23234             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23235         }
23236         if(!this.editable){
23237             this.editable = true;
23238             this.setEditable(false);
23239         }  
23240         
23241         
23242         if (typeof(this.events.add.listeners) != 'undefined') {
23243             
23244             this.addicon = this.wrap.createChild(
23245                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23246        
23247             this.addicon.on('click', function(e) {
23248                 this.fireEvent('add', this);
23249             }, this);
23250         }
23251         if (typeof(this.events.edit.listeners) != 'undefined') {
23252             
23253             this.editicon = this.wrap.createChild(
23254                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23255             if (this.addicon) {
23256                 this.editicon.setStyle('margin-left', '40px');
23257             }
23258             this.editicon.on('click', function(e) {
23259                 
23260                 // we fire even  if inothing is selected..
23261                 this.fireEvent('edit', this, this.lastData );
23262                 
23263             }, this);
23264         }
23265         
23266         
23267         
23268     },
23269
23270     // private
23271     initEvents : function(){
23272         Roo.form.ComboBox.superclass.initEvents.call(this);
23273
23274         this.keyNav = new Roo.KeyNav(this.el, {
23275             "up" : function(e){
23276                 this.inKeyMode = true;
23277                 this.selectPrev();
23278             },
23279
23280             "down" : function(e){
23281                 if(!this.isExpanded()){
23282                     this.onTriggerClick();
23283                 }else{
23284                     this.inKeyMode = true;
23285                     this.selectNext();
23286                 }
23287             },
23288
23289             "enter" : function(e){
23290                 this.onViewClick();
23291                 //return true;
23292             },
23293
23294             "esc" : function(e){
23295                 this.collapse();
23296             },
23297
23298             "tab" : function(e){
23299                 this.onViewClick(false);
23300                 this.fireEvent("specialkey", this, e);
23301                 return true;
23302             },
23303
23304             scope : this,
23305
23306             doRelay : function(foo, bar, hname){
23307                 if(hname == 'down' || this.scope.isExpanded()){
23308                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23309                 }
23310                 return true;
23311             },
23312
23313             forceKeyDown: true
23314         });
23315         this.queryDelay = Math.max(this.queryDelay || 10,
23316                 this.mode == 'local' ? 10 : 250);
23317         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23318         if(this.typeAhead){
23319             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23320         }
23321         if(this.editable !== false){
23322             this.el.on("keyup", this.onKeyUp, this);
23323         }
23324         if(this.forceSelection){
23325             this.on('blur', this.doForce, this);
23326         }
23327     },
23328
23329     onDestroy : function(){
23330         if(this.view){
23331             this.view.setStore(null);
23332             this.view.el.removeAllListeners();
23333             this.view.el.remove();
23334             this.view.purgeListeners();
23335         }
23336         if(this.list){
23337             this.list.destroy();
23338         }
23339         if(this.store){
23340             this.store.un('beforeload', this.onBeforeLoad, this);
23341             this.store.un('load', this.onLoad, this);
23342             this.store.un('loadexception', this.onLoadException, this);
23343         }
23344         Roo.form.ComboBox.superclass.onDestroy.call(this);
23345     },
23346
23347     // private
23348     fireKey : function(e){
23349         if(e.isNavKeyPress() && !this.list.isVisible()){
23350             this.fireEvent("specialkey", this, e);
23351         }
23352     },
23353
23354     // private
23355     onResize: function(w, h){
23356         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23357         
23358         if(typeof w != 'number'){
23359             // we do not handle it!?!?
23360             return;
23361         }
23362         var tw = this.trigger.getWidth();
23363         tw += this.addicon ? this.addicon.getWidth() : 0;
23364         tw += this.editicon ? this.editicon.getWidth() : 0;
23365         var x = w - tw;
23366         this.el.setWidth( this.adjustWidth('input', x));
23367             
23368         this.trigger.setStyle('left', x+'px');
23369         
23370         if(this.list && this.listWidth === undefined){
23371             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23372             this.list.setWidth(lw);
23373             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23374         }
23375         
23376     
23377         
23378     },
23379
23380     /**
23381      * Allow or prevent the user from directly editing the field text.  If false is passed,
23382      * the user will only be able to select from the items defined in the dropdown list.  This method
23383      * is the runtime equivalent of setting the 'editable' config option at config time.
23384      * @param {Boolean} value True to allow the user to directly edit the field text
23385      */
23386     setEditable : function(value){
23387         if(value == this.editable){
23388             return;
23389         }
23390         this.editable = value;
23391         if(!value){
23392             this.el.dom.setAttribute('readOnly', true);
23393             this.el.on('mousedown', this.onTriggerClick,  this);
23394             this.el.addClass('x-combo-noedit');
23395         }else{
23396             this.el.dom.setAttribute('readOnly', false);
23397             this.el.un('mousedown', this.onTriggerClick,  this);
23398             this.el.removeClass('x-combo-noedit');
23399         }
23400     },
23401
23402     // private
23403     onBeforeLoad : function(){
23404         if(!this.hasFocus){
23405             return;
23406         }
23407         this.innerList.update(this.loadingText ?
23408                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23409         this.restrictHeight();
23410         this.selectedIndex = -1;
23411     },
23412
23413     // private
23414     onLoad : function(){
23415         if(!this.hasFocus){
23416             return;
23417         }
23418         if(this.store.getCount() > 0){
23419             this.expand();
23420             this.restrictHeight();
23421             if(this.lastQuery == this.allQuery){
23422                 if(this.editable){
23423                     this.el.dom.select();
23424                 }
23425                 if(!this.selectByValue(this.value, true)){
23426                     this.select(0, true);
23427                 }
23428             }else{
23429                 this.selectNext();
23430                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23431                     this.taTask.delay(this.typeAheadDelay);
23432                 }
23433             }
23434         }else{
23435             this.onEmptyResults();
23436         }
23437         //this.el.focus();
23438     },
23439     // private
23440     onLoadException : function()
23441     {
23442         this.collapse();
23443         Roo.log(this.store.reader.jsonData);
23444         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23445             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23446         }
23447         
23448         
23449     },
23450     // private
23451     onTypeAhead : function(){
23452         if(this.store.getCount() > 0){
23453             var r = this.store.getAt(0);
23454             var newValue = r.data[this.displayField];
23455             var len = newValue.length;
23456             var selStart = this.getRawValue().length;
23457             if(selStart != len){
23458                 this.setRawValue(newValue);
23459                 this.selectText(selStart, newValue.length);
23460             }
23461         }
23462     },
23463
23464     // private
23465     onSelect : function(record, index){
23466         if(this.fireEvent('beforeselect', this, record, index) !== false){
23467             this.setFromData(index > -1 ? record.data : false);
23468             this.collapse();
23469             this.fireEvent('select', this, record, index);
23470         }
23471     },
23472
23473     /**
23474      * Returns the currently selected field value or empty string if no value is set.
23475      * @return {String} value The selected value
23476      */
23477     getValue : function(){
23478         if(this.valueField){
23479             return typeof this.value != 'undefined' ? this.value : '';
23480         }
23481         return Roo.form.ComboBox.superclass.getValue.call(this);
23482     },
23483
23484     /**
23485      * Clears any text/value currently set in the field
23486      */
23487     clearValue : function(){
23488         if(this.hiddenField){
23489             this.hiddenField.value = '';
23490         }
23491         this.value = '';
23492         this.setRawValue('');
23493         this.lastSelectionText = '';
23494         
23495     },
23496
23497     /**
23498      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23499      * will be displayed in the field.  If the value does not match the data value of an existing item,
23500      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23501      * Otherwise the field will be blank (although the value will still be set).
23502      * @param {String} value The value to match
23503      */
23504     setValue : function(v){
23505         var text = v;
23506         if(this.valueField){
23507             var r = this.findRecord(this.valueField, v);
23508             if(r){
23509                 text = r.data[this.displayField];
23510             }else if(this.valueNotFoundText !== undefined){
23511                 text = this.valueNotFoundText;
23512             }
23513         }
23514         this.lastSelectionText = text;
23515         if(this.hiddenField){
23516             this.hiddenField.value = v;
23517         }
23518         Roo.form.ComboBox.superclass.setValue.call(this, text);
23519         this.value = v;
23520     },
23521     /**
23522      * @property {Object} the last set data for the element
23523      */
23524     
23525     lastData : false,
23526     /**
23527      * Sets the value of the field based on a object which is related to the record format for the store.
23528      * @param {Object} value the value to set as. or false on reset?
23529      */
23530     setFromData : function(o){
23531         var dv = ''; // display value
23532         var vv = ''; // value value..
23533         this.lastData = o;
23534         if (this.displayField) {
23535             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23536         } else {
23537             // this is an error condition!!!
23538             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23539         }
23540         
23541         if(this.valueField){
23542             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23543         }
23544         if(this.hiddenField){
23545             this.hiddenField.value = vv;
23546             
23547             this.lastSelectionText = dv;
23548             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23549             this.value = vv;
23550             return;
23551         }
23552         // no hidden field.. - we store the value in 'value', but still display
23553         // display field!!!!
23554         this.lastSelectionText = dv;
23555         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23556         this.value = vv;
23557         
23558         
23559     },
23560     // private
23561     reset : function(){
23562         // overridden so that last data is reset..
23563         this.setValue(this.resetValue);
23564         this.clearInvalid();
23565         this.lastData = false;
23566         if (this.view) {
23567             this.view.clearSelections();
23568         }
23569     },
23570     // private
23571     findRecord : function(prop, value){
23572         var record;
23573         if(this.store.getCount() > 0){
23574             this.store.each(function(r){
23575                 if(r.data[prop] == value){
23576                     record = r;
23577                     return false;
23578                 }
23579                 return true;
23580             });
23581         }
23582         return record;
23583     },
23584     
23585     getName: function()
23586     {
23587         // returns hidden if it's set..
23588         if (!this.rendered) {return ''};
23589         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23590         
23591     },
23592     // private
23593     onViewMove : function(e, t){
23594         this.inKeyMode = false;
23595     },
23596
23597     // private
23598     onViewOver : function(e, t){
23599         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23600             return;
23601         }
23602         var item = this.view.findItemFromChild(t);
23603         if(item){
23604             var index = this.view.indexOf(item);
23605             this.select(index, false);
23606         }
23607     },
23608
23609     // private
23610     onViewClick : function(doFocus)
23611     {
23612         var index = this.view.getSelectedIndexes()[0];
23613         var r = this.store.getAt(index);
23614         if(r){
23615             this.onSelect(r, index);
23616         }
23617         if(doFocus !== false && !this.blockFocus){
23618             this.el.focus();
23619         }
23620     },
23621
23622     // private
23623     restrictHeight : function(){
23624         this.innerList.dom.style.height = '';
23625         var inner = this.innerList.dom;
23626         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23627         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23628         this.list.beginUpdate();
23629         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23630         this.list.alignTo(this.el, this.listAlign);
23631         this.list.endUpdate();
23632     },
23633
23634     // private
23635     onEmptyResults : function(){
23636         this.collapse();
23637     },
23638
23639     /**
23640      * Returns true if the dropdown list is expanded, else false.
23641      */
23642     isExpanded : function(){
23643         return this.list.isVisible();
23644     },
23645
23646     /**
23647      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23648      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23649      * @param {String} value The data value of the item to select
23650      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23651      * selected item if it is not currently in view (defaults to true)
23652      * @return {Boolean} True if the value matched an item in the list, else false
23653      */
23654     selectByValue : function(v, scrollIntoView){
23655         if(v !== undefined && v !== null){
23656             var r = this.findRecord(this.valueField || this.displayField, v);
23657             if(r){
23658                 this.select(this.store.indexOf(r), scrollIntoView);
23659                 return true;
23660             }
23661         }
23662         return false;
23663     },
23664
23665     /**
23666      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23667      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23668      * @param {Number} index The zero-based index of the list item to select
23669      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23670      * selected item if it is not currently in view (defaults to true)
23671      */
23672     select : function(index, scrollIntoView){
23673         this.selectedIndex = index;
23674         this.view.select(index);
23675         if(scrollIntoView !== false){
23676             var el = this.view.getNode(index);
23677             if(el){
23678                 this.innerList.scrollChildIntoView(el, false);
23679             }
23680         }
23681     },
23682
23683     // private
23684     selectNext : function(){
23685         var ct = this.store.getCount();
23686         if(ct > 0){
23687             if(this.selectedIndex == -1){
23688                 this.select(0);
23689             }else if(this.selectedIndex < ct-1){
23690                 this.select(this.selectedIndex+1);
23691             }
23692         }
23693     },
23694
23695     // private
23696     selectPrev : function(){
23697         var ct = this.store.getCount();
23698         if(ct > 0){
23699             if(this.selectedIndex == -1){
23700                 this.select(0);
23701             }else if(this.selectedIndex != 0){
23702                 this.select(this.selectedIndex-1);
23703             }
23704         }
23705     },
23706
23707     // private
23708     onKeyUp : function(e){
23709         if(this.editable !== false && !e.isSpecialKey()){
23710             this.lastKey = e.getKey();
23711             this.dqTask.delay(this.queryDelay);
23712         }
23713     },
23714
23715     // private
23716     validateBlur : function(){
23717         return !this.list || !this.list.isVisible();   
23718     },
23719
23720     // private
23721     initQuery : function(){
23722         this.doQuery(this.getRawValue());
23723     },
23724
23725     // private
23726     doForce : function(){
23727         if(this.el.dom.value.length > 0){
23728             this.el.dom.value =
23729                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23730              
23731         }
23732     },
23733
23734     /**
23735      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23736      * query allowing the query action to be canceled if needed.
23737      * @param {String} query The SQL query to execute
23738      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23739      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23740      * saved in the current store (defaults to false)
23741      */
23742     doQuery : function(q, forceAll){
23743         if(q === undefined || q === null){
23744             q = '';
23745         }
23746         var qe = {
23747             query: q,
23748             forceAll: forceAll,
23749             combo: this,
23750             cancel:false
23751         };
23752         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23753             return false;
23754         }
23755         q = qe.query;
23756         forceAll = qe.forceAll;
23757         if(forceAll === true || (q.length >= this.minChars)){
23758             if(this.lastQuery != q || this.alwaysQuery){
23759                 this.lastQuery = q;
23760                 if(this.mode == 'local'){
23761                     this.selectedIndex = -1;
23762                     if(forceAll){
23763                         this.store.clearFilter();
23764                     }else{
23765                         this.store.filter(this.displayField, q);
23766                     }
23767                     this.onLoad();
23768                 }else{
23769                     this.store.baseParams[this.queryParam] = q;
23770                     this.store.load({
23771                         params: this.getParams(q)
23772                     });
23773                     this.expand();
23774                 }
23775             }else{
23776                 this.selectedIndex = -1;
23777                 this.onLoad();   
23778             }
23779         }
23780     },
23781
23782     // private
23783     getParams : function(q){
23784         var p = {};
23785         //p[this.queryParam] = q;
23786         if(this.pageSize){
23787             p.start = 0;
23788             p.limit = this.pageSize;
23789         }
23790         return p;
23791     },
23792
23793     /**
23794      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23795      */
23796     collapse : function(){
23797         if(!this.isExpanded()){
23798             return;
23799         }
23800         this.list.hide();
23801         Roo.get(document).un('mousedown', this.collapseIf, this);
23802         Roo.get(document).un('mousewheel', this.collapseIf, this);
23803         if (!this.editable) {
23804             Roo.get(document).un('keydown', this.listKeyPress, this);
23805         }
23806         this.fireEvent('collapse', this);
23807     },
23808
23809     // private
23810     collapseIf : function(e){
23811         if(!e.within(this.wrap) && !e.within(this.list)){
23812             this.collapse();
23813         }
23814     },
23815
23816     /**
23817      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23818      */
23819     expand : function(){
23820         if(this.isExpanded() || !this.hasFocus){
23821             return;
23822         }
23823         this.list.alignTo(this.el, this.listAlign);
23824         this.list.show();
23825         Roo.get(document).on('mousedown', this.collapseIf, this);
23826         Roo.get(document).on('mousewheel', this.collapseIf, this);
23827         if (!this.editable) {
23828             Roo.get(document).on('keydown', this.listKeyPress, this);
23829         }
23830         
23831         this.fireEvent('expand', this);
23832     },
23833
23834     // private
23835     // Implements the default empty TriggerField.onTriggerClick function
23836     onTriggerClick : function(){
23837         if(this.disabled){
23838             return;
23839         }
23840         if(this.isExpanded()){
23841             this.collapse();
23842             if (!this.blockFocus) {
23843                 this.el.focus();
23844             }
23845             
23846         }else {
23847             this.hasFocus = true;
23848             if(this.triggerAction == 'all') {
23849                 this.doQuery(this.allQuery, true);
23850             } else {
23851                 this.doQuery(this.getRawValue());
23852             }
23853             if (!this.blockFocus) {
23854                 this.el.focus();
23855             }
23856         }
23857     },
23858     listKeyPress : function(e)
23859     {
23860         //Roo.log('listkeypress');
23861         // scroll to first matching element based on key pres..
23862         if (e.isSpecialKey()) {
23863             return false;
23864         }
23865         var k = String.fromCharCode(e.getKey()).toUpperCase();
23866         //Roo.log(k);
23867         var match  = false;
23868         var csel = this.view.getSelectedNodes();
23869         var cselitem = false;
23870         if (csel.length) {
23871             var ix = this.view.indexOf(csel[0]);
23872             cselitem  = this.store.getAt(ix);
23873             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23874                 cselitem = false;
23875             }
23876             
23877         }
23878         
23879         this.store.each(function(v) { 
23880             if (cselitem) {
23881                 // start at existing selection.
23882                 if (cselitem.id == v.id) {
23883                     cselitem = false;
23884                 }
23885                 return;
23886             }
23887                 
23888             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23889                 match = this.store.indexOf(v);
23890                 return false;
23891             }
23892         }, this);
23893         
23894         if (match === false) {
23895             return true; // no more action?
23896         }
23897         // scroll to?
23898         this.view.select(match);
23899         var sn = Roo.get(this.view.getSelectedNodes()[0]);
23900         sn.scrollIntoView(sn.dom.parentNode, false);
23901     }
23902
23903     /** 
23904     * @cfg {Boolean} grow 
23905     * @hide 
23906     */
23907     /** 
23908     * @cfg {Number} growMin 
23909     * @hide 
23910     */
23911     /** 
23912     * @cfg {Number} growMax 
23913     * @hide 
23914     */
23915     /**
23916      * @hide
23917      * @method autoSize
23918      */
23919 });/*
23920  * Copyright(c) 2010-2012, Roo J Solutions Limited
23921  *
23922  * Licence LGPL
23923  *
23924  */
23925
23926 /**
23927  * @class Roo.form.ComboBoxArray
23928  * @extends Roo.form.TextField
23929  * A facebook style adder... for lists of email / people / countries  etc...
23930  * pick multiple items from a combo box, and shows each one.
23931  *
23932  *  Fred [x]  Brian [x]  [Pick another |v]
23933  *
23934  *
23935  *  For this to work: it needs various extra information
23936  *    - normal combo problay has
23937  *      name, hiddenName
23938  *    + displayField, valueField
23939  *
23940  *    For our purpose...
23941  *
23942  *
23943  *   If we change from 'extends' to wrapping...
23944  *   
23945  *  
23946  *
23947  
23948  
23949  * @constructor
23950  * Create a new ComboBoxArray.
23951  * @param {Object} config Configuration options
23952  */
23953  
23954
23955 Roo.form.ComboBoxArray = function(config)
23956 {
23957     this.addEvents({
23958         /**
23959          * @event beforeremove
23960          * Fires before remove the value from the list
23961              * @param {Roo.form.ComboBoxArray} _self This combo box array
23962              * @param {Roo.form.ComboBoxArray.Item} item removed item
23963              */
23964         'beforeremove' : true,
23965         /**
23966          * @event remove
23967          * Fires when remove the value from the list
23968              * @param {Roo.form.ComboBoxArray} _self This combo box array
23969              * @param {Roo.form.ComboBoxArray.Item} item removed item
23970              */
23971         'remove' : true
23972         
23973         
23974     });
23975     
23976     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23977     
23978     this.items = new Roo.util.MixedCollection(false);
23979     
23980     // construct the child combo...
23981     
23982     
23983     
23984     
23985    
23986     
23987 }
23988
23989  
23990 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23991
23992     /**
23993      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23994      */
23995     
23996     lastData : false,
23997     
23998     // behavies liek a hiddne field
23999     inputType:      'hidden',
24000     /**
24001      * @cfg {Number} width The width of the box that displays the selected element
24002      */ 
24003     width:          300,
24004
24005     
24006     
24007     /**
24008      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24009      */
24010     name : false,
24011     /**
24012      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24013      */
24014     hiddenName : false,
24015     
24016     
24017     // private the array of items that are displayed..
24018     items  : false,
24019     // private - the hidden field el.
24020     hiddenEl : false,
24021     // private - the filed el..
24022     el : false,
24023     
24024     //validateValue : function() { return true; }, // all values are ok!
24025     //onAddClick: function() { },
24026     
24027     onRender : function(ct, position) 
24028     {
24029         
24030         // create the standard hidden element
24031         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24032         
24033         
24034         // give fake names to child combo;
24035         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24036         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24037         
24038         this.combo = Roo.factory(this.combo, Roo.form);
24039         this.combo.onRender(ct, position);
24040         if (typeof(this.combo.width) != 'undefined') {
24041             this.combo.onResize(this.combo.width,0);
24042         }
24043         
24044         this.combo.initEvents();
24045         
24046         // assigned so form know we need to do this..
24047         this.store          = this.combo.store;
24048         this.valueField     = this.combo.valueField;
24049         this.displayField   = this.combo.displayField ;
24050         
24051         
24052         this.combo.wrap.addClass('x-cbarray-grp');
24053         
24054         var cbwrap = this.combo.wrap.createChild(
24055             {tag: 'div', cls: 'x-cbarray-cb'},
24056             this.combo.el.dom
24057         );
24058         
24059              
24060         this.hiddenEl = this.combo.wrap.createChild({
24061             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24062         });
24063         this.el = this.combo.wrap.createChild({
24064             tag: 'input',  type:'hidden' , name: this.name, value : ''
24065         });
24066          //   this.el.dom.removeAttribute("name");
24067         
24068         
24069         this.outerWrap = this.combo.wrap;
24070         this.wrap = cbwrap;
24071         
24072         this.outerWrap.setWidth(this.width);
24073         this.outerWrap.dom.removeChild(this.el.dom);
24074         
24075         this.wrap.dom.appendChild(this.el.dom);
24076         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24077         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24078         
24079         this.combo.trigger.setStyle('position','relative');
24080         this.combo.trigger.setStyle('left', '0px');
24081         this.combo.trigger.setStyle('top', '2px');
24082         
24083         this.combo.el.setStyle('vertical-align', 'text-bottom');
24084         
24085         //this.trigger.setStyle('vertical-align', 'top');
24086         
24087         // this should use the code from combo really... on('add' ....)
24088         if (this.adder) {
24089             
24090         
24091             this.adder = this.outerWrap.createChild(
24092                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24093             var _t = this;
24094             this.adder.on('click', function(e) {
24095                 _t.fireEvent('adderclick', this, e);
24096             }, _t);
24097         }
24098         //var _t = this;
24099         //this.adder.on('click', this.onAddClick, _t);
24100         
24101         
24102         this.combo.on('select', function(cb, rec, ix) {
24103             this.addItem(rec.data);
24104             
24105             cb.setValue('');
24106             cb.el.dom.value = '';
24107             //cb.lastData = rec.data;
24108             // add to list
24109             
24110         }, this);
24111         
24112         
24113     },
24114     
24115     
24116     getName: function()
24117     {
24118         // returns hidden if it's set..
24119         if (!this.rendered) {return ''};
24120         return  this.hiddenName ? this.hiddenName : this.name;
24121         
24122     },
24123     
24124     
24125     onResize: function(w, h){
24126         
24127         return;
24128         // not sure if this is needed..
24129         //this.combo.onResize(w,h);
24130         
24131         if(typeof w != 'number'){
24132             // we do not handle it!?!?
24133             return;
24134         }
24135         var tw = this.combo.trigger.getWidth();
24136         tw += this.addicon ? this.addicon.getWidth() : 0;
24137         tw += this.editicon ? this.editicon.getWidth() : 0;
24138         var x = w - tw;
24139         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24140             
24141         this.combo.trigger.setStyle('left', '0px');
24142         
24143         if(this.list && this.listWidth === undefined){
24144             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24145             this.list.setWidth(lw);
24146             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24147         }
24148         
24149     
24150         
24151     },
24152     
24153     addItem: function(rec)
24154     {
24155         var valueField = this.combo.valueField;
24156         var displayField = this.combo.displayField;
24157         if (this.items.indexOfKey(rec[valueField]) > -1) {
24158             //console.log("GOT " + rec.data.id);
24159             return;
24160         }
24161         
24162         var x = new Roo.form.ComboBoxArray.Item({
24163             //id : rec[this.idField],
24164             data : rec,
24165             displayField : displayField ,
24166             tipField : displayField ,
24167             cb : this
24168         });
24169         // use the 
24170         this.items.add(rec[valueField],x);
24171         // add it before the element..
24172         this.updateHiddenEl();
24173         x.render(this.outerWrap, this.wrap.dom);
24174         // add the image handler..
24175     },
24176     
24177     updateHiddenEl : function()
24178     {
24179         this.validate();
24180         if (!this.hiddenEl) {
24181             return;
24182         }
24183         var ar = [];
24184         var idField = this.combo.valueField;
24185         
24186         this.items.each(function(f) {
24187             ar.push(f.data[idField]);
24188            
24189         });
24190         this.hiddenEl.dom.value = ar.join(',');
24191         this.validate();
24192     },
24193     
24194     reset : function()
24195     {
24196         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24197         this.items.each(function(f) {
24198            f.remove(); 
24199         });
24200         this.el.dom.value = '';
24201         if (this.hiddenEl) {
24202             this.hiddenEl.dom.value = '';
24203         }
24204         
24205     },
24206     getValue: function()
24207     {
24208         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24209     },
24210     setValue: function(v) // not a valid action - must use addItems..
24211     {
24212          
24213         this.reset();
24214         
24215         
24216         
24217         if (this.store.isLocal && (typeof(v) == 'string')) {
24218             // then we can use the store to find the values..
24219             // comma seperated at present.. this needs to allow JSON based encoding..
24220             this.hiddenEl.value  = v;
24221             var v_ar = [];
24222             Roo.each(v.split(','), function(k) {
24223                 Roo.log("CHECK " + this.valueField + ',' + k);
24224                 var li = this.store.query(this.valueField, k);
24225                 if (!li.length) {
24226                     return;
24227                 }
24228                 var add = {};
24229                 add[this.valueField] = k;
24230                 add[this.displayField] = li.item(0).data[this.displayField];
24231                 
24232                 this.addItem(add);
24233             }, this) 
24234              
24235         }
24236         if (typeof(v) == 'object' ) {
24237             // then let's assume it's an array of objects..
24238             Roo.each(v, function(l) {
24239                 this.addItem(l);
24240             }, this);
24241              
24242         }
24243         
24244         
24245     },
24246     setFromData: function(v)
24247     {
24248         // this recieves an object, if setValues is called.
24249         this.reset();
24250         this.el.dom.value = v[this.displayField];
24251         this.hiddenEl.dom.value = v[this.valueField];
24252         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24253             return;
24254         }
24255         var kv = v[this.valueField];
24256         var dv = v[this.displayField];
24257         kv = typeof(kv) != 'string' ? '' : kv;
24258         dv = typeof(dv) != 'string' ? '' : dv;
24259         
24260         
24261         var keys = kv.split(',');
24262         var display = dv.split(',');
24263         for (var i = 0 ; i < keys.length; i++) {
24264             
24265             add = {};
24266             add[this.valueField] = keys[i];
24267             add[this.displayField] = display[i];
24268             this.addItem(add);
24269         }
24270       
24271         
24272     },
24273     
24274     /**
24275      * Validates the combox array value
24276      * @return {Boolean} True if the value is valid, else false
24277      */
24278     validate : function(){
24279         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24280             this.clearInvalid();
24281             return true;
24282         }
24283         return false;
24284     },
24285     
24286     validateValue : function(value){
24287         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24288         
24289     },
24290     
24291     /*@
24292      * overide
24293      * 
24294      */
24295     isDirty : function() {
24296         if(this.disabled) {
24297             return false;
24298         }
24299         
24300         try {
24301             var d = Roo.decode(String(this.originalValue));
24302         } catch (e) {
24303             return String(this.getValue()) !== String(this.originalValue);
24304         }
24305         
24306         var originalValue = [];
24307         
24308         for (var i = 0; i < d.length; i++){
24309             originalValue.push(d[i][this.valueField]);
24310         }
24311         
24312         return String(this.getValue()) !== String(originalValue.join(','));
24313         
24314     }
24315     
24316 });
24317
24318
24319
24320 /**
24321  * @class Roo.form.ComboBoxArray.Item
24322  * @extends Roo.BoxComponent
24323  * A selected item in the list
24324  *  Fred [x]  Brian [x]  [Pick another |v]
24325  * 
24326  * @constructor
24327  * Create a new item.
24328  * @param {Object} config Configuration options
24329  */
24330  
24331 Roo.form.ComboBoxArray.Item = function(config) {
24332     config.id = Roo.id();
24333     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24334 }
24335
24336 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24337     data : {},
24338     cb: false,
24339     displayField : false,
24340     tipField : false,
24341     
24342     
24343     defaultAutoCreate : {
24344         tag: 'div',
24345         cls: 'x-cbarray-item',
24346         cn : [ 
24347             { tag: 'div' },
24348             {
24349                 tag: 'img',
24350                 width:16,
24351                 height : 16,
24352                 src : Roo.BLANK_IMAGE_URL ,
24353                 align: 'center'
24354             }
24355         ]
24356         
24357     },
24358     
24359  
24360     onRender : function(ct, position)
24361     {
24362         Roo.form.Field.superclass.onRender.call(this, ct, position);
24363         
24364         if(!this.el){
24365             var cfg = this.getAutoCreate();
24366             this.el = ct.createChild(cfg, position);
24367         }
24368         
24369         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24370         
24371         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24372             this.cb.renderer(this.data) :
24373             String.format('{0}',this.data[this.displayField]);
24374         
24375             
24376         this.el.child('div').dom.setAttribute('qtip',
24377                         String.format('{0}',this.data[this.tipField])
24378         );
24379         
24380         this.el.child('img').on('click', this.remove, this);
24381         
24382     },
24383    
24384     remove : function()
24385     {
24386         if(this.cb.disabled){
24387             return;
24388         }
24389         
24390         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24391             this.cb.items.remove(this);
24392             this.el.child('img').un('click', this.remove, this);
24393             this.el.remove();
24394             this.cb.updateHiddenEl();
24395
24396             this.cb.fireEvent('remove', this.cb, this);
24397         }
24398         
24399     }
24400 });/*
24401  * Based on:
24402  * Ext JS Library 1.1.1
24403  * Copyright(c) 2006-2007, Ext JS, LLC.
24404  *
24405  * Originally Released Under LGPL - original licence link has changed is not relivant.
24406  *
24407  * Fork - LGPL
24408  * <script type="text/javascript">
24409  */
24410 /**
24411  * @class Roo.form.Checkbox
24412  * @extends Roo.form.Field
24413  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24414  * @constructor
24415  * Creates a new Checkbox
24416  * @param {Object} config Configuration options
24417  */
24418 Roo.form.Checkbox = function(config){
24419     Roo.form.Checkbox.superclass.constructor.call(this, config);
24420     this.addEvents({
24421         /**
24422          * @event check
24423          * Fires when the checkbox is checked or unchecked.
24424              * @param {Roo.form.Checkbox} this This checkbox
24425              * @param {Boolean} checked The new checked value
24426              */
24427         check : true
24428     });
24429 };
24430
24431 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24432     /**
24433      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24434      */
24435     focusClass : undefined,
24436     /**
24437      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24438      */
24439     fieldClass: "x-form-field",
24440     /**
24441      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24442      */
24443     checked: false,
24444     /**
24445      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24446      * {tag: "input", type: "checkbox", autocomplete: "off"})
24447      */
24448     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24449     /**
24450      * @cfg {String} boxLabel The text that appears beside the checkbox
24451      */
24452     boxLabel : "",
24453     /**
24454      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24455      */  
24456     inputValue : '1',
24457     /**
24458      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24459      */
24460      valueOff: '0', // value when not checked..
24461
24462     actionMode : 'viewEl', 
24463     //
24464     // private
24465     itemCls : 'x-menu-check-item x-form-item',
24466     groupClass : 'x-menu-group-item',
24467     inputType : 'hidden',
24468     
24469     
24470     inSetChecked: false, // check that we are not calling self...
24471     
24472     inputElement: false, // real input element?
24473     basedOn: false, // ????
24474     
24475     isFormField: true, // not sure where this is needed!!!!
24476
24477     onResize : function(){
24478         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24479         if(!this.boxLabel){
24480             this.el.alignTo(this.wrap, 'c-c');
24481         }
24482     },
24483
24484     initEvents : function(){
24485         Roo.form.Checkbox.superclass.initEvents.call(this);
24486         this.el.on("click", this.onClick,  this);
24487         this.el.on("change", this.onClick,  this);
24488     },
24489
24490
24491     getResizeEl : function(){
24492         return this.wrap;
24493     },
24494
24495     getPositionEl : function(){
24496         return this.wrap;
24497     },
24498
24499     // private
24500     onRender : function(ct, position){
24501         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24502         /*
24503         if(this.inputValue !== undefined){
24504             this.el.dom.value = this.inputValue;
24505         }
24506         */
24507         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24508         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24509         var viewEl = this.wrap.createChild({ 
24510             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24511         this.viewEl = viewEl;   
24512         this.wrap.on('click', this.onClick,  this); 
24513         
24514         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24515         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24516         
24517         
24518         
24519         if(this.boxLabel){
24520             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24521         //    viewEl.on('click', this.onClick,  this); 
24522         }
24523         //if(this.checked){
24524             this.setChecked(this.checked);
24525         //}else{
24526             //this.checked = this.el.dom;
24527         //}
24528
24529     },
24530
24531     // private
24532     initValue : Roo.emptyFn,
24533
24534     /**
24535      * Returns the checked state of the checkbox.
24536      * @return {Boolean} True if checked, else false
24537      */
24538     getValue : function(){
24539         if(this.el){
24540             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24541         }
24542         return this.valueOff;
24543         
24544     },
24545
24546         // private
24547     onClick : function(){ 
24548         if (this.disabled) {
24549             return;
24550         }
24551         this.setChecked(!this.checked);
24552
24553         //if(this.el.dom.checked != this.checked){
24554         //    this.setValue(this.el.dom.checked);
24555        // }
24556     },
24557
24558     /**
24559      * Sets the checked state of the checkbox.
24560      * On is always based on a string comparison between inputValue and the param.
24561      * @param {Boolean/String} value - the value to set 
24562      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24563      */
24564     setValue : function(v,suppressEvent){
24565         
24566         
24567         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24568         //if(this.el && this.el.dom){
24569         //    this.el.dom.checked = this.checked;
24570         //    this.el.dom.defaultChecked = this.checked;
24571         //}
24572         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24573         //this.fireEvent("check", this, this.checked);
24574     },
24575     // private..
24576     setChecked : function(state,suppressEvent)
24577     {
24578         if (this.inSetChecked) {
24579             this.checked = state;
24580             return;
24581         }
24582         
24583     
24584         if(this.wrap){
24585             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24586         }
24587         this.checked = state;
24588         if(suppressEvent !== true){
24589             this.fireEvent('check', this, state);
24590         }
24591         this.inSetChecked = true;
24592         this.el.dom.value = state ? this.inputValue : this.valueOff;
24593         this.inSetChecked = false;
24594         
24595     },
24596     // handle setting of hidden value by some other method!!?!?
24597     setFromHidden: function()
24598     {
24599         if(!this.el){
24600             return;
24601         }
24602         //console.log("SET FROM HIDDEN");
24603         //alert('setFrom hidden');
24604         this.setValue(this.el.dom.value);
24605     },
24606     
24607     onDestroy : function()
24608     {
24609         if(this.viewEl){
24610             Roo.get(this.viewEl).remove();
24611         }
24612          
24613         Roo.form.Checkbox.superclass.onDestroy.call(this);
24614     }
24615
24616 });/*
24617  * Based on:
24618  * Ext JS Library 1.1.1
24619  * Copyright(c) 2006-2007, Ext JS, LLC.
24620  *
24621  * Originally Released Under LGPL - original licence link has changed is not relivant.
24622  *
24623  * Fork - LGPL
24624  * <script type="text/javascript">
24625  */
24626  
24627 /**
24628  * @class Roo.form.Radio
24629  * @extends Roo.form.Checkbox
24630  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24631  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24632  * @constructor
24633  * Creates a new Radio
24634  * @param {Object} config Configuration options
24635  */
24636 Roo.form.Radio = function(){
24637     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24638 };
24639 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24640     inputType: 'radio',
24641
24642     /**
24643      * If this radio is part of a group, it will return the selected value
24644      * @return {String}
24645      */
24646     getGroupValue : function(){
24647         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24648     },
24649     
24650     
24651     onRender : function(ct, position){
24652         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24653         
24654         if(this.inputValue !== undefined){
24655             this.el.dom.value = this.inputValue;
24656         }
24657          
24658         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24659         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24660         //var viewEl = this.wrap.createChild({ 
24661         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24662         //this.viewEl = viewEl;   
24663         //this.wrap.on('click', this.onClick,  this); 
24664         
24665         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24666         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24667         
24668         
24669         
24670         if(this.boxLabel){
24671             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24672         //    viewEl.on('click', this.onClick,  this); 
24673         }
24674          if(this.checked){
24675             this.el.dom.checked =   'checked' ;
24676         }
24677          
24678     } 
24679     
24680     
24681 });//<script type="text/javascript">
24682
24683 /*
24684  * Based  Ext JS Library 1.1.1
24685  * Copyright(c) 2006-2007, Ext JS, LLC.
24686  * LGPL
24687  *
24688  */
24689  
24690 /**
24691  * @class Roo.HtmlEditorCore
24692  * @extends Roo.Component
24693  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24694  *
24695  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24696  */
24697
24698 Roo.HtmlEditorCore = function(config){
24699     
24700     
24701     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24702     
24703     
24704     this.addEvents({
24705         /**
24706          * @event initialize
24707          * Fires when the editor is fully initialized (including the iframe)
24708          * @param {Roo.HtmlEditorCore} this
24709          */
24710         initialize: true,
24711         /**
24712          * @event activate
24713          * Fires when the editor is first receives the focus. Any insertion must wait
24714          * until after this event.
24715          * @param {Roo.HtmlEditorCore} this
24716          */
24717         activate: true,
24718          /**
24719          * @event beforesync
24720          * Fires before the textarea is updated with content from the editor iframe. Return false
24721          * to cancel the sync.
24722          * @param {Roo.HtmlEditorCore} this
24723          * @param {String} html
24724          */
24725         beforesync: true,
24726          /**
24727          * @event beforepush
24728          * Fires before the iframe editor is updated with content from the textarea. Return false
24729          * to cancel the push.
24730          * @param {Roo.HtmlEditorCore} this
24731          * @param {String} html
24732          */
24733         beforepush: true,
24734          /**
24735          * @event sync
24736          * Fires when the textarea is updated with content from the editor iframe.
24737          * @param {Roo.HtmlEditorCore} this
24738          * @param {String} html
24739          */
24740         sync: true,
24741          /**
24742          * @event push
24743          * Fires when the iframe editor is updated with content from the textarea.
24744          * @param {Roo.HtmlEditorCore} this
24745          * @param {String} html
24746          */
24747         push: true,
24748         
24749         /**
24750          * @event editorevent
24751          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24752          * @param {Roo.HtmlEditorCore} this
24753          */
24754         editorevent: true
24755         
24756     });
24757     
24758     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24759     
24760     // defaults : white / black...
24761     this.applyBlacklists();
24762     
24763     
24764     
24765 };
24766
24767
24768 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24769
24770
24771      /**
24772      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24773      */
24774     
24775     owner : false,
24776     
24777      /**
24778      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24779      *                        Roo.resizable.
24780      */
24781     resizable : false,
24782      /**
24783      * @cfg {Number} height (in pixels)
24784      */   
24785     height: 300,
24786    /**
24787      * @cfg {Number} width (in pixels)
24788      */   
24789     width: 500,
24790     
24791     /**
24792      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24793      * 
24794      */
24795     stylesheets: false,
24796     
24797     // id of frame..
24798     frameId: false,
24799     
24800     // private properties
24801     validationEvent : false,
24802     deferHeight: true,
24803     initialized : false,
24804     activated : false,
24805     sourceEditMode : false,
24806     onFocus : Roo.emptyFn,
24807     iframePad:3,
24808     hideMode:'offsets',
24809     
24810     clearUp: true,
24811     
24812     // blacklist + whitelisted elements..
24813     black: false,
24814     white: false,
24815      
24816     
24817
24818     /**
24819      * Protected method that will not generally be called directly. It
24820      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24821      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24822      */
24823     getDocMarkup : function(){
24824         // body styles..
24825         var st = '';
24826         
24827         // inherit styels from page...?? 
24828         if (this.stylesheets === false) {
24829             
24830             Roo.get(document.head).select('style').each(function(node) {
24831                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24832             });
24833             
24834             Roo.get(document.head).select('link').each(function(node) { 
24835                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24836             });
24837             
24838         } else if (!this.stylesheets.length) {
24839                 // simple..
24840                 st = '<style type="text/css">' +
24841                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24842                    '</style>';
24843         } else { 
24844             
24845         }
24846         
24847         st +=  '<style type="text/css">' +
24848             'IMG { cursor: pointer } ' +
24849         '</style>';
24850
24851         
24852         return '<html><head>' + st  +
24853             //<style type="text/css">' +
24854             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24855             //'</style>' +
24856             ' </head><body class="roo-htmleditor-body"></body></html>';
24857     },
24858
24859     // private
24860     onRender : function(ct, position)
24861     {
24862         var _t = this;
24863         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24864         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24865         
24866         
24867         this.el.dom.style.border = '0 none';
24868         this.el.dom.setAttribute('tabIndex', -1);
24869         this.el.addClass('x-hidden hide');
24870         
24871         
24872         
24873         if(Roo.isIE){ // fix IE 1px bogus margin
24874             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24875         }
24876        
24877         
24878         this.frameId = Roo.id();
24879         
24880          
24881         
24882         var iframe = this.owner.wrap.createChild({
24883             tag: 'iframe',
24884             cls: 'form-control', // bootstrap..
24885             id: this.frameId,
24886             name: this.frameId,
24887             frameBorder : 'no',
24888             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24889         }, this.el
24890         );
24891         
24892         
24893         this.iframe = iframe.dom;
24894
24895          this.assignDocWin();
24896         
24897         this.doc.designMode = 'on';
24898        
24899         this.doc.open();
24900         this.doc.write(this.getDocMarkup());
24901         this.doc.close();
24902
24903         
24904         var task = { // must defer to wait for browser to be ready
24905             run : function(){
24906                 //console.log("run task?" + this.doc.readyState);
24907                 this.assignDocWin();
24908                 if(this.doc.body || this.doc.readyState == 'complete'){
24909                     try {
24910                         this.doc.designMode="on";
24911                     } catch (e) {
24912                         return;
24913                     }
24914                     Roo.TaskMgr.stop(task);
24915                     this.initEditor.defer(10, this);
24916                 }
24917             },
24918             interval : 10,
24919             duration: 10000,
24920             scope: this
24921         };
24922         Roo.TaskMgr.start(task);
24923
24924     },
24925
24926     // private
24927     onResize : function(w, h)
24928     {
24929          Roo.log('resize: ' +w + ',' + h );
24930         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24931         if(!this.iframe){
24932             return;
24933         }
24934         if(typeof w == 'number'){
24935             
24936             this.iframe.style.width = w + 'px';
24937         }
24938         if(typeof h == 'number'){
24939             
24940             this.iframe.style.height = h + 'px';
24941             if(this.doc){
24942                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24943             }
24944         }
24945         
24946     },
24947
24948     /**
24949      * Toggles the editor between standard and source edit mode.
24950      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24951      */
24952     toggleSourceEdit : function(sourceEditMode){
24953         
24954         this.sourceEditMode = sourceEditMode === true;
24955         
24956         if(this.sourceEditMode){
24957  
24958             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24959             
24960         }else{
24961             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24962             //this.iframe.className = '';
24963             this.deferFocus();
24964         }
24965         //this.setSize(this.owner.wrap.getSize());
24966         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24967     },
24968
24969     
24970   
24971
24972     /**
24973      * Protected method that will not generally be called directly. If you need/want
24974      * custom HTML cleanup, this is the method you should override.
24975      * @param {String} html The HTML to be cleaned
24976      * return {String} The cleaned HTML
24977      */
24978     cleanHtml : function(html){
24979         html = String(html);
24980         if(html.length > 5){
24981             if(Roo.isSafari){ // strip safari nonsense
24982                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24983             }
24984         }
24985         if(html == '&nbsp;'){
24986             html = '';
24987         }
24988         return html;
24989     },
24990
24991     /**
24992      * HTML Editor -> Textarea
24993      * Protected method that will not generally be called directly. Syncs the contents
24994      * of the editor iframe with the textarea.
24995      */
24996     syncValue : function(){
24997         if(this.initialized){
24998             var bd = (this.doc.body || this.doc.documentElement);
24999             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25000             var html = bd.innerHTML;
25001             if(Roo.isSafari){
25002                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25003                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25004                 if(m && m[1]){
25005                     html = '<div style="'+m[0]+'">' + html + '</div>';
25006                 }
25007             }
25008             html = this.cleanHtml(html);
25009             // fix up the special chars.. normaly like back quotes in word...
25010             // however we do not want to do this with chinese..
25011             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25012                 var cc = b.charCodeAt();
25013                 if (
25014                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25015                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25016                     (cc >= 0xf900 && cc < 0xfb00 )
25017                 ) {
25018                         return b;
25019                 }
25020                 return "&#"+cc+";" 
25021             });
25022             if(this.owner.fireEvent('beforesync', this, html) !== false){
25023                 this.el.dom.value = html;
25024                 this.owner.fireEvent('sync', this, html);
25025             }
25026         }
25027     },
25028
25029     /**
25030      * Protected method that will not generally be called directly. Pushes the value of the textarea
25031      * into the iframe editor.
25032      */
25033     pushValue : function(){
25034         if(this.initialized){
25035             var v = this.el.dom.value.trim();
25036             
25037 //            if(v.length < 1){
25038 //                v = '&#160;';
25039 //            }
25040             
25041             if(this.owner.fireEvent('beforepush', this, v) !== false){
25042                 var d = (this.doc.body || this.doc.documentElement);
25043                 d.innerHTML = v;
25044                 this.cleanUpPaste();
25045                 this.el.dom.value = d.innerHTML;
25046                 this.owner.fireEvent('push', this, v);
25047             }
25048         }
25049     },
25050
25051     // private
25052     deferFocus : function(){
25053         this.focus.defer(10, this);
25054     },
25055
25056     // doc'ed in Field
25057     focus : function(){
25058         if(this.win && !this.sourceEditMode){
25059             this.win.focus();
25060         }else{
25061             this.el.focus();
25062         }
25063     },
25064     
25065     assignDocWin: function()
25066     {
25067         var iframe = this.iframe;
25068         
25069          if(Roo.isIE){
25070             this.doc = iframe.contentWindow.document;
25071             this.win = iframe.contentWindow;
25072         } else {
25073 //            if (!Roo.get(this.frameId)) {
25074 //                return;
25075 //            }
25076 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25077 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25078             
25079             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25080                 return;
25081             }
25082             
25083             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25084             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25085         }
25086     },
25087     
25088     // private
25089     initEditor : function(){
25090         //console.log("INIT EDITOR");
25091         this.assignDocWin();
25092         
25093         
25094         
25095         this.doc.designMode="on";
25096         this.doc.open();
25097         this.doc.write(this.getDocMarkup());
25098         this.doc.close();
25099         
25100         var dbody = (this.doc.body || this.doc.documentElement);
25101         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25102         // this copies styles from the containing element into thsi one..
25103         // not sure why we need all of this..
25104         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25105         
25106         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25107         //ss['background-attachment'] = 'fixed'; // w3c
25108         dbody.bgProperties = 'fixed'; // ie
25109         //Roo.DomHelper.applyStyles(dbody, ss);
25110         Roo.EventManager.on(this.doc, {
25111             //'mousedown': this.onEditorEvent,
25112             'mouseup': this.onEditorEvent,
25113             'dblclick': this.onEditorEvent,
25114             'click': this.onEditorEvent,
25115             'keyup': this.onEditorEvent,
25116             buffer:100,
25117             scope: this
25118         });
25119         if(Roo.isGecko){
25120             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25121         }
25122         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25123             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25124         }
25125         this.initialized = true;
25126
25127         this.owner.fireEvent('initialize', this);
25128         this.pushValue();
25129     },
25130
25131     // private
25132     onDestroy : function(){
25133         
25134         
25135         
25136         if(this.rendered){
25137             
25138             //for (var i =0; i < this.toolbars.length;i++) {
25139             //    // fixme - ask toolbars for heights?
25140             //    this.toolbars[i].onDestroy();
25141            // }
25142             
25143             //this.wrap.dom.innerHTML = '';
25144             //this.wrap.remove();
25145         }
25146     },
25147
25148     // private
25149     onFirstFocus : function(){
25150         
25151         this.assignDocWin();
25152         
25153         
25154         this.activated = true;
25155          
25156     
25157         if(Roo.isGecko){ // prevent silly gecko errors
25158             this.win.focus();
25159             var s = this.win.getSelection();
25160             if(!s.focusNode || s.focusNode.nodeType != 3){
25161                 var r = s.getRangeAt(0);
25162                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25163                 r.collapse(true);
25164                 this.deferFocus();
25165             }
25166             try{
25167                 this.execCmd('useCSS', true);
25168                 this.execCmd('styleWithCSS', false);
25169             }catch(e){}
25170         }
25171         this.owner.fireEvent('activate', this);
25172     },
25173
25174     // private
25175     adjustFont: function(btn){
25176         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25177         //if(Roo.isSafari){ // safari
25178         //    adjust *= 2;
25179        // }
25180         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25181         if(Roo.isSafari){ // safari
25182             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25183             v =  (v < 10) ? 10 : v;
25184             v =  (v > 48) ? 48 : v;
25185             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25186             
25187         }
25188         
25189         
25190         v = Math.max(1, v+adjust);
25191         
25192         this.execCmd('FontSize', v  );
25193     },
25194
25195     onEditorEvent : function(e)
25196     {
25197         this.owner.fireEvent('editorevent', this, e);
25198       //  this.updateToolbar();
25199         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25200     },
25201
25202     insertTag : function(tg)
25203     {
25204         // could be a bit smarter... -> wrap the current selected tRoo..
25205         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25206             
25207             range = this.createRange(this.getSelection());
25208             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25209             wrappingNode.appendChild(range.extractContents());
25210             range.insertNode(wrappingNode);
25211
25212             return;
25213             
25214             
25215             
25216         }
25217         this.execCmd("formatblock",   tg);
25218         
25219     },
25220     
25221     insertText : function(txt)
25222     {
25223         
25224         
25225         var range = this.createRange();
25226         range.deleteContents();
25227                //alert(Sender.getAttribute('label'));
25228                
25229         range.insertNode(this.doc.createTextNode(txt));
25230     } ,
25231     
25232      
25233
25234     /**
25235      * Executes a Midas editor command on the editor document and performs necessary focus and
25236      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25237      * @param {String} cmd The Midas command
25238      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25239      */
25240     relayCmd : function(cmd, value){
25241         this.win.focus();
25242         this.execCmd(cmd, value);
25243         this.owner.fireEvent('editorevent', this);
25244         //this.updateToolbar();
25245         this.owner.deferFocus();
25246     },
25247
25248     /**
25249      * Executes a Midas editor command directly on the editor document.
25250      * For visual commands, you should use {@link #relayCmd} instead.
25251      * <b>This should only be called after the editor is initialized.</b>
25252      * @param {String} cmd The Midas command
25253      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25254      */
25255     execCmd : function(cmd, value){
25256         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25257         this.syncValue();
25258     },
25259  
25260  
25261    
25262     /**
25263      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25264      * to insert tRoo.
25265      * @param {String} text | dom node.. 
25266      */
25267     insertAtCursor : function(text)
25268     {
25269         
25270         
25271         
25272         if(!this.activated){
25273             return;
25274         }
25275         /*
25276         if(Roo.isIE){
25277             this.win.focus();
25278             var r = this.doc.selection.createRange();
25279             if(r){
25280                 r.collapse(true);
25281                 r.pasteHTML(text);
25282                 this.syncValue();
25283                 this.deferFocus();
25284             
25285             }
25286             return;
25287         }
25288         */
25289         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25290             this.win.focus();
25291             
25292             
25293             // from jquery ui (MIT licenced)
25294             var range, node;
25295             var win = this.win;
25296             
25297             if (win.getSelection && win.getSelection().getRangeAt) {
25298                 range = win.getSelection().getRangeAt(0);
25299                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25300                 range.insertNode(node);
25301             } else if (win.document.selection && win.document.selection.createRange) {
25302                 // no firefox support
25303                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25304                 win.document.selection.createRange().pasteHTML(txt);
25305             } else {
25306                 // no firefox support
25307                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25308                 this.execCmd('InsertHTML', txt);
25309             } 
25310             
25311             this.syncValue();
25312             
25313             this.deferFocus();
25314         }
25315     },
25316  // private
25317     mozKeyPress : function(e){
25318         if(e.ctrlKey){
25319             var c = e.getCharCode(), cmd;
25320           
25321             if(c > 0){
25322                 c = String.fromCharCode(c).toLowerCase();
25323                 switch(c){
25324                     case 'b':
25325                         cmd = 'bold';
25326                         break;
25327                     case 'i':
25328                         cmd = 'italic';
25329                         break;
25330                     
25331                     case 'u':
25332                         cmd = 'underline';
25333                         break;
25334                     
25335                     case 'v':
25336                         this.cleanUpPaste.defer(100, this);
25337                         return;
25338                         
25339                 }
25340                 if(cmd){
25341                     this.win.focus();
25342                     this.execCmd(cmd);
25343                     this.deferFocus();
25344                     e.preventDefault();
25345                 }
25346                 
25347             }
25348         }
25349     },
25350
25351     // private
25352     fixKeys : function(){ // load time branching for fastest keydown performance
25353         if(Roo.isIE){
25354             return function(e){
25355                 var k = e.getKey(), r;
25356                 if(k == e.TAB){
25357                     e.stopEvent();
25358                     r = this.doc.selection.createRange();
25359                     if(r){
25360                         r.collapse(true);
25361                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25362                         this.deferFocus();
25363                     }
25364                     return;
25365                 }
25366                 
25367                 if(k == e.ENTER){
25368                     r = this.doc.selection.createRange();
25369                     if(r){
25370                         var target = r.parentElement();
25371                         if(!target || target.tagName.toLowerCase() != 'li'){
25372                             e.stopEvent();
25373                             r.pasteHTML('<br />');
25374                             r.collapse(false);
25375                             r.select();
25376                         }
25377                     }
25378                 }
25379                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25380                     this.cleanUpPaste.defer(100, this);
25381                     return;
25382                 }
25383                 
25384                 
25385             };
25386         }else if(Roo.isOpera){
25387             return function(e){
25388                 var k = e.getKey();
25389                 if(k == e.TAB){
25390                     e.stopEvent();
25391                     this.win.focus();
25392                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25393                     this.deferFocus();
25394                 }
25395                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25396                     this.cleanUpPaste.defer(100, this);
25397                     return;
25398                 }
25399                 
25400             };
25401         }else if(Roo.isSafari){
25402             return function(e){
25403                 var k = e.getKey();
25404                 
25405                 if(k == e.TAB){
25406                     e.stopEvent();
25407                     this.execCmd('InsertText','\t');
25408                     this.deferFocus();
25409                     return;
25410                 }
25411                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25412                     this.cleanUpPaste.defer(100, this);
25413                     return;
25414                 }
25415                 
25416              };
25417         }
25418     }(),
25419     
25420     getAllAncestors: function()
25421     {
25422         var p = this.getSelectedNode();
25423         var a = [];
25424         if (!p) {
25425             a.push(p); // push blank onto stack..
25426             p = this.getParentElement();
25427         }
25428         
25429         
25430         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25431             a.push(p);
25432             p = p.parentNode;
25433         }
25434         a.push(this.doc.body);
25435         return a;
25436     },
25437     lastSel : false,
25438     lastSelNode : false,
25439     
25440     
25441     getSelection : function() 
25442     {
25443         this.assignDocWin();
25444         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25445     },
25446     
25447     getSelectedNode: function() 
25448     {
25449         // this may only work on Gecko!!!
25450         
25451         // should we cache this!!!!
25452         
25453         
25454         
25455          
25456         var range = this.createRange(this.getSelection()).cloneRange();
25457         
25458         if (Roo.isIE) {
25459             var parent = range.parentElement();
25460             while (true) {
25461                 var testRange = range.duplicate();
25462                 testRange.moveToElementText(parent);
25463                 if (testRange.inRange(range)) {
25464                     break;
25465                 }
25466                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25467                     break;
25468                 }
25469                 parent = parent.parentElement;
25470             }
25471             return parent;
25472         }
25473         
25474         // is ancestor a text element.
25475         var ac =  range.commonAncestorContainer;
25476         if (ac.nodeType == 3) {
25477             ac = ac.parentNode;
25478         }
25479         
25480         var ar = ac.childNodes;
25481          
25482         var nodes = [];
25483         var other_nodes = [];
25484         var has_other_nodes = false;
25485         for (var i=0;i<ar.length;i++) {
25486             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25487                 continue;
25488             }
25489             // fullly contained node.
25490             
25491             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25492                 nodes.push(ar[i]);
25493                 continue;
25494             }
25495             
25496             // probably selected..
25497             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25498                 other_nodes.push(ar[i]);
25499                 continue;
25500             }
25501             // outer..
25502             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25503                 continue;
25504             }
25505             
25506             
25507             has_other_nodes = true;
25508         }
25509         if (!nodes.length && other_nodes.length) {
25510             nodes= other_nodes;
25511         }
25512         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25513             return false;
25514         }
25515         
25516         return nodes[0];
25517     },
25518     createRange: function(sel)
25519     {
25520         // this has strange effects when using with 
25521         // top toolbar - not sure if it's a great idea.
25522         //this.editor.contentWindow.focus();
25523         if (typeof sel != "undefined") {
25524             try {
25525                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25526             } catch(e) {
25527                 return this.doc.createRange();
25528             }
25529         } else {
25530             return this.doc.createRange();
25531         }
25532     },
25533     getParentElement: function()
25534     {
25535         
25536         this.assignDocWin();
25537         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25538         
25539         var range = this.createRange(sel);
25540          
25541         try {
25542             var p = range.commonAncestorContainer;
25543             while (p.nodeType == 3) { // text node
25544                 p = p.parentNode;
25545             }
25546             return p;
25547         } catch (e) {
25548             return null;
25549         }
25550     
25551     },
25552     /***
25553      *
25554      * Range intersection.. the hard stuff...
25555      *  '-1' = before
25556      *  '0' = hits..
25557      *  '1' = after.
25558      *         [ -- selected range --- ]
25559      *   [fail]                        [fail]
25560      *
25561      *    basically..
25562      *      if end is before start or  hits it. fail.
25563      *      if start is after end or hits it fail.
25564      *
25565      *   if either hits (but other is outside. - then it's not 
25566      *   
25567      *    
25568      **/
25569     
25570     
25571     // @see http://www.thismuchiknow.co.uk/?p=64.
25572     rangeIntersectsNode : function(range, node)
25573     {
25574         var nodeRange = node.ownerDocument.createRange();
25575         try {
25576             nodeRange.selectNode(node);
25577         } catch (e) {
25578             nodeRange.selectNodeContents(node);
25579         }
25580     
25581         var rangeStartRange = range.cloneRange();
25582         rangeStartRange.collapse(true);
25583     
25584         var rangeEndRange = range.cloneRange();
25585         rangeEndRange.collapse(false);
25586     
25587         var nodeStartRange = nodeRange.cloneRange();
25588         nodeStartRange.collapse(true);
25589     
25590         var nodeEndRange = nodeRange.cloneRange();
25591         nodeEndRange.collapse(false);
25592     
25593         return rangeStartRange.compareBoundaryPoints(
25594                  Range.START_TO_START, nodeEndRange) == -1 &&
25595                rangeEndRange.compareBoundaryPoints(
25596                  Range.START_TO_START, nodeStartRange) == 1;
25597         
25598          
25599     },
25600     rangeCompareNode : function(range, node)
25601     {
25602         var nodeRange = node.ownerDocument.createRange();
25603         try {
25604             nodeRange.selectNode(node);
25605         } catch (e) {
25606             nodeRange.selectNodeContents(node);
25607         }
25608         
25609         
25610         range.collapse(true);
25611     
25612         nodeRange.collapse(true);
25613      
25614         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25615         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25616          
25617         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25618         
25619         var nodeIsBefore   =  ss == 1;
25620         var nodeIsAfter    = ee == -1;
25621         
25622         if (nodeIsBefore && nodeIsAfter) {
25623             return 0; // outer
25624         }
25625         if (!nodeIsBefore && nodeIsAfter) {
25626             return 1; //right trailed.
25627         }
25628         
25629         if (nodeIsBefore && !nodeIsAfter) {
25630             return 2;  // left trailed.
25631         }
25632         // fully contined.
25633         return 3;
25634     },
25635
25636     // private? - in a new class?
25637     cleanUpPaste :  function()
25638     {
25639         // cleans up the whole document..
25640         Roo.log('cleanuppaste');
25641         
25642         this.cleanUpChildren(this.doc.body);
25643         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25644         if (clean != this.doc.body.innerHTML) {
25645             this.doc.body.innerHTML = clean;
25646         }
25647         
25648     },
25649     
25650     cleanWordChars : function(input) {// change the chars to hex code
25651         var he = Roo.HtmlEditorCore;
25652         
25653         var output = input;
25654         Roo.each(he.swapCodes, function(sw) { 
25655             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25656             
25657             output = output.replace(swapper, sw[1]);
25658         });
25659         
25660         return output;
25661     },
25662     
25663     
25664     cleanUpChildren : function (n)
25665     {
25666         if (!n.childNodes.length) {
25667             return;
25668         }
25669         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25670            this.cleanUpChild(n.childNodes[i]);
25671         }
25672     },
25673     
25674     
25675         
25676     
25677     cleanUpChild : function (node)
25678     {
25679         var ed = this;
25680         //console.log(node);
25681         if (node.nodeName == "#text") {
25682             // clean up silly Windows -- stuff?
25683             return; 
25684         }
25685         if (node.nodeName == "#comment") {
25686             node.parentNode.removeChild(node);
25687             // clean up silly Windows -- stuff?
25688             return; 
25689         }
25690         var lcname = node.tagName.toLowerCase();
25691         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25692         // whitelist of tags..
25693         
25694         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25695             // remove node.
25696             node.parentNode.removeChild(node);
25697             return;
25698             
25699         }
25700         
25701         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25702         
25703         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25704         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25705         
25706         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25707         //    remove_keep_children = true;
25708         //}
25709         
25710         if (remove_keep_children) {
25711             this.cleanUpChildren(node);
25712             // inserts everything just before this node...
25713             while (node.childNodes.length) {
25714                 var cn = node.childNodes[0];
25715                 node.removeChild(cn);
25716                 node.parentNode.insertBefore(cn, node);
25717             }
25718             node.parentNode.removeChild(node);
25719             return;
25720         }
25721         
25722         if (!node.attributes || !node.attributes.length) {
25723             this.cleanUpChildren(node);
25724             return;
25725         }
25726         
25727         function cleanAttr(n,v)
25728         {
25729             
25730             if (v.match(/^\./) || v.match(/^\//)) {
25731                 return;
25732             }
25733             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25734                 return;
25735             }
25736             if (v.match(/^#/)) {
25737                 return;
25738             }
25739 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25740             node.removeAttribute(n);
25741             
25742         }
25743         
25744         var cwhite = this.cwhite;
25745         var cblack = this.cblack;
25746             
25747         function cleanStyle(n,v)
25748         {
25749             if (v.match(/expression/)) { //XSS?? should we even bother..
25750                 node.removeAttribute(n);
25751                 return;
25752             }
25753             
25754             var parts = v.split(/;/);
25755             var clean = [];
25756             
25757             Roo.each(parts, function(p) {
25758                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25759                 if (!p.length) {
25760                     return true;
25761                 }
25762                 var l = p.split(':').shift().replace(/\s+/g,'');
25763                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25764                 
25765                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25766 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25767                     //node.removeAttribute(n);
25768                     return true;
25769                 }
25770                 //Roo.log()
25771                 // only allow 'c whitelisted system attributes'
25772                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25773 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25774                     //node.removeAttribute(n);
25775                     return true;
25776                 }
25777                 
25778                 
25779                  
25780                 
25781                 clean.push(p);
25782                 return true;
25783             });
25784             if (clean.length) { 
25785                 node.setAttribute(n, clean.join(';'));
25786             } else {
25787                 node.removeAttribute(n);
25788             }
25789             
25790         }
25791         
25792         
25793         for (var i = node.attributes.length-1; i > -1 ; i--) {
25794             var a = node.attributes[i];
25795             //console.log(a);
25796             
25797             if (a.name.toLowerCase().substr(0,2)=='on')  {
25798                 node.removeAttribute(a.name);
25799                 continue;
25800             }
25801             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25802                 node.removeAttribute(a.name);
25803                 continue;
25804             }
25805             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25806                 cleanAttr(a.name,a.value); // fixme..
25807                 continue;
25808             }
25809             if (a.name == 'style') {
25810                 cleanStyle(a.name,a.value);
25811                 continue;
25812             }
25813             /// clean up MS crap..
25814             // tecnically this should be a list of valid class'es..
25815             
25816             
25817             if (a.name == 'class') {
25818                 if (a.value.match(/^Mso/)) {
25819                     node.className = '';
25820                 }
25821                 
25822                 if (a.value.match(/body/)) {
25823                     node.className = '';
25824                 }
25825                 continue;
25826             }
25827             
25828             // style cleanup!?
25829             // class cleanup?
25830             
25831         }
25832         
25833         
25834         this.cleanUpChildren(node);
25835         
25836         
25837     },
25838     
25839     /**
25840      * Clean up MS wordisms...
25841      */
25842     cleanWord : function(node)
25843     {
25844         
25845         
25846         if (!node) {
25847             this.cleanWord(this.doc.body);
25848             return;
25849         }
25850         if (node.nodeName == "#text") {
25851             // clean up silly Windows -- stuff?
25852             return; 
25853         }
25854         if (node.nodeName == "#comment") {
25855             node.parentNode.removeChild(node);
25856             // clean up silly Windows -- stuff?
25857             return; 
25858         }
25859         
25860         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25861             node.parentNode.removeChild(node);
25862             return;
25863         }
25864         
25865         // remove - but keep children..
25866         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25867             while (node.childNodes.length) {
25868                 var cn = node.childNodes[0];
25869                 node.removeChild(cn);
25870                 node.parentNode.insertBefore(cn, node);
25871             }
25872             node.parentNode.removeChild(node);
25873             this.iterateChildren(node, this.cleanWord);
25874             return;
25875         }
25876         // clean styles
25877         if (node.className.length) {
25878             
25879             var cn = node.className.split(/\W+/);
25880             var cna = [];
25881             Roo.each(cn, function(cls) {
25882                 if (cls.match(/Mso[a-zA-Z]+/)) {
25883                     return;
25884                 }
25885                 cna.push(cls);
25886             });
25887             node.className = cna.length ? cna.join(' ') : '';
25888             if (!cna.length) {
25889                 node.removeAttribute("class");
25890             }
25891         }
25892         
25893         if (node.hasAttribute("lang")) {
25894             node.removeAttribute("lang");
25895         }
25896         
25897         if (node.hasAttribute("style")) {
25898             
25899             var styles = node.getAttribute("style").split(";");
25900             var nstyle = [];
25901             Roo.each(styles, function(s) {
25902                 if (!s.match(/:/)) {
25903                     return;
25904                 }
25905                 var kv = s.split(":");
25906                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25907                     return;
25908                 }
25909                 // what ever is left... we allow.
25910                 nstyle.push(s);
25911             });
25912             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25913             if (!nstyle.length) {
25914                 node.removeAttribute('style');
25915             }
25916         }
25917         this.iterateChildren(node, this.cleanWord);
25918         
25919         
25920         
25921     },
25922     /**
25923      * iterateChildren of a Node, calling fn each time, using this as the scole..
25924      * @param {DomNode} node node to iterate children of.
25925      * @param {Function} fn method of this class to call on each item.
25926      */
25927     iterateChildren : function(node, fn)
25928     {
25929         if (!node.childNodes.length) {
25930                 return;
25931         }
25932         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25933            fn.call(this, node.childNodes[i])
25934         }
25935     },
25936     
25937     
25938     /**
25939      * cleanTableWidths.
25940      *
25941      * Quite often pasting from word etc.. results in tables with column and widths.
25942      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25943      *
25944      */
25945     cleanTableWidths : function(node)
25946     {
25947          
25948          
25949         if (!node) {
25950             this.cleanTableWidths(this.doc.body);
25951             return;
25952         }
25953         
25954         // ignore list...
25955         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25956             return; 
25957         }
25958         Roo.log(node.tagName);
25959         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25960             this.iterateChildren(node, this.cleanTableWidths);
25961             return;
25962         }
25963         if (node.hasAttribute('width')) {
25964             node.removeAttribute('width');
25965         }
25966         
25967          
25968         if (node.hasAttribute("style")) {
25969             // pretty basic...
25970             
25971             var styles = node.getAttribute("style").split(";");
25972             var nstyle = [];
25973             Roo.each(styles, function(s) {
25974                 if (!s.match(/:/)) {
25975                     return;
25976                 }
25977                 var kv = s.split(":");
25978                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25979                     return;
25980                 }
25981                 // what ever is left... we allow.
25982                 nstyle.push(s);
25983             });
25984             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25985             if (!nstyle.length) {
25986                 node.removeAttribute('style');
25987             }
25988         }
25989         
25990         this.iterateChildren(node, this.cleanTableWidths);
25991         
25992         
25993     },
25994     
25995     
25996     
25997     
25998     domToHTML : function(currentElement, depth, nopadtext) {
25999         
26000         depth = depth || 0;
26001         nopadtext = nopadtext || false;
26002     
26003         if (!currentElement) {
26004             return this.domToHTML(this.doc.body);
26005         }
26006         
26007         //Roo.log(currentElement);
26008         var j;
26009         var allText = false;
26010         var nodeName = currentElement.nodeName;
26011         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26012         
26013         if  (nodeName == '#text') {
26014             
26015             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26016         }
26017         
26018         
26019         var ret = '';
26020         if (nodeName != 'BODY') {
26021              
26022             var i = 0;
26023             // Prints the node tagName, such as <A>, <IMG>, etc
26024             if (tagName) {
26025                 var attr = [];
26026                 for(i = 0; i < currentElement.attributes.length;i++) {
26027                     // quoting?
26028                     var aname = currentElement.attributes.item(i).name;
26029                     if (!currentElement.attributes.item(i).value.length) {
26030                         continue;
26031                     }
26032                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26033                 }
26034                 
26035                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26036             } 
26037             else {
26038                 
26039                 // eack
26040             }
26041         } else {
26042             tagName = false;
26043         }
26044         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26045             return ret;
26046         }
26047         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26048             nopadtext = true;
26049         }
26050         
26051         
26052         // Traverse the tree
26053         i = 0;
26054         var currentElementChild = currentElement.childNodes.item(i);
26055         var allText = true;
26056         var innerHTML  = '';
26057         lastnode = '';
26058         while (currentElementChild) {
26059             // Formatting code (indent the tree so it looks nice on the screen)
26060             var nopad = nopadtext;
26061             if (lastnode == 'SPAN') {
26062                 nopad  = true;
26063             }
26064             // text
26065             if  (currentElementChild.nodeName == '#text') {
26066                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26067                 toadd = nopadtext ? toadd : toadd.trim();
26068                 if (!nopad && toadd.length > 80) {
26069                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26070                 }
26071                 innerHTML  += toadd;
26072                 
26073                 i++;
26074                 currentElementChild = currentElement.childNodes.item(i);
26075                 lastNode = '';
26076                 continue;
26077             }
26078             allText = false;
26079             
26080             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26081                 
26082             // Recursively traverse the tree structure of the child node
26083             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26084             lastnode = currentElementChild.nodeName;
26085             i++;
26086             currentElementChild=currentElement.childNodes.item(i);
26087         }
26088         
26089         ret += innerHTML;
26090         
26091         if (!allText) {
26092                 // The remaining code is mostly for formatting the tree
26093             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26094         }
26095         
26096         
26097         if (tagName) {
26098             ret+= "</"+tagName+">";
26099         }
26100         return ret;
26101         
26102     },
26103         
26104     applyBlacklists : function()
26105     {
26106         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26107         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26108         
26109         this.white = [];
26110         this.black = [];
26111         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26112             if (b.indexOf(tag) > -1) {
26113                 return;
26114             }
26115             this.white.push(tag);
26116             
26117         }, this);
26118         
26119         Roo.each(w, function(tag) {
26120             if (b.indexOf(tag) > -1) {
26121                 return;
26122             }
26123             if (this.white.indexOf(tag) > -1) {
26124                 return;
26125             }
26126             this.white.push(tag);
26127             
26128         }, this);
26129         
26130         
26131         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26132             if (w.indexOf(tag) > -1) {
26133                 return;
26134             }
26135             this.black.push(tag);
26136             
26137         }, this);
26138         
26139         Roo.each(b, function(tag) {
26140             if (w.indexOf(tag) > -1) {
26141                 return;
26142             }
26143             if (this.black.indexOf(tag) > -1) {
26144                 return;
26145             }
26146             this.black.push(tag);
26147             
26148         }, this);
26149         
26150         
26151         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26152         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26153         
26154         this.cwhite = [];
26155         this.cblack = [];
26156         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26157             if (b.indexOf(tag) > -1) {
26158                 return;
26159             }
26160             this.cwhite.push(tag);
26161             
26162         }, this);
26163         
26164         Roo.each(w, function(tag) {
26165             if (b.indexOf(tag) > -1) {
26166                 return;
26167             }
26168             if (this.cwhite.indexOf(tag) > -1) {
26169                 return;
26170             }
26171             this.cwhite.push(tag);
26172             
26173         }, this);
26174         
26175         
26176         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26177             if (w.indexOf(tag) > -1) {
26178                 return;
26179             }
26180             this.cblack.push(tag);
26181             
26182         }, this);
26183         
26184         Roo.each(b, function(tag) {
26185             if (w.indexOf(tag) > -1) {
26186                 return;
26187             }
26188             if (this.cblack.indexOf(tag) > -1) {
26189                 return;
26190             }
26191             this.cblack.push(tag);
26192             
26193         }, this);
26194     },
26195     
26196     setStylesheets : function(stylesheets)
26197     {
26198         if(typeof(stylesheets) == 'string'){
26199             Roo.get(this.iframe.contentDocument.head).createChild({
26200                 tag : 'link',
26201                 rel : 'stylesheet',
26202                 type : 'text/css',
26203                 href : stylesheets
26204             });
26205             
26206             return;
26207         }
26208         var _this = this;
26209      
26210         Roo.each(stylesheets, function(s) {
26211             if(!s.length){
26212                 return;
26213             }
26214             
26215             Roo.get(_this.iframe.contentDocument.head).createChild({
26216                 tag : 'link',
26217                 rel : 'stylesheet',
26218                 type : 'text/css',
26219                 href : s
26220             });
26221         });
26222
26223         
26224     },
26225     
26226     removeStylesheets : function()
26227     {
26228         var _this = this;
26229         
26230         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26231             s.remove();
26232         });
26233     }
26234     
26235     // hide stuff that is not compatible
26236     /**
26237      * @event blur
26238      * @hide
26239      */
26240     /**
26241      * @event change
26242      * @hide
26243      */
26244     /**
26245      * @event focus
26246      * @hide
26247      */
26248     /**
26249      * @event specialkey
26250      * @hide
26251      */
26252     /**
26253      * @cfg {String} fieldClass @hide
26254      */
26255     /**
26256      * @cfg {String} focusClass @hide
26257      */
26258     /**
26259      * @cfg {String} autoCreate @hide
26260      */
26261     /**
26262      * @cfg {String} inputType @hide
26263      */
26264     /**
26265      * @cfg {String} invalidClass @hide
26266      */
26267     /**
26268      * @cfg {String} invalidText @hide
26269      */
26270     /**
26271      * @cfg {String} msgFx @hide
26272      */
26273     /**
26274      * @cfg {String} validateOnBlur @hide
26275      */
26276 });
26277
26278 Roo.HtmlEditorCore.white = [
26279         'area', 'br', 'img', 'input', 'hr', 'wbr',
26280         
26281        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26282        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26283        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26284        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26285        'table',   'ul',         'xmp', 
26286        
26287        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26288       'thead',   'tr', 
26289      
26290       'dir', 'menu', 'ol', 'ul', 'dl',
26291        
26292       'embed',  'object'
26293 ];
26294
26295
26296 Roo.HtmlEditorCore.black = [
26297     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26298         'applet', // 
26299         'base',   'basefont', 'bgsound', 'blink',  'body', 
26300         'frame',  'frameset', 'head',    'html',   'ilayer', 
26301         'iframe', 'layer',  'link',     'meta',    'object',   
26302         'script', 'style' ,'title',  'xml' // clean later..
26303 ];
26304 Roo.HtmlEditorCore.clean = [
26305     'script', 'style', 'title', 'xml'
26306 ];
26307 Roo.HtmlEditorCore.remove = [
26308     'font'
26309 ];
26310 // attributes..
26311
26312 Roo.HtmlEditorCore.ablack = [
26313     'on'
26314 ];
26315     
26316 Roo.HtmlEditorCore.aclean = [ 
26317     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26318 ];
26319
26320 // protocols..
26321 Roo.HtmlEditorCore.pwhite= [
26322         'http',  'https',  'mailto'
26323 ];
26324
26325 // white listed style attributes.
26326 Roo.HtmlEditorCore.cwhite= [
26327       //  'text-align', /// default is to allow most things..
26328       
26329          
26330 //        'font-size'//??
26331 ];
26332
26333 // black listed style attributes.
26334 Roo.HtmlEditorCore.cblack= [
26335       //  'font-size' -- this can be set by the project 
26336 ];
26337
26338
26339 Roo.HtmlEditorCore.swapCodes   =[ 
26340     [    8211, "--" ], 
26341     [    8212, "--" ], 
26342     [    8216,  "'" ],  
26343     [    8217, "'" ],  
26344     [    8220, '"' ],  
26345     [    8221, '"' ],  
26346     [    8226, "*" ],  
26347     [    8230, "..." ]
26348 ]; 
26349
26350     //<script type="text/javascript">
26351
26352 /*
26353  * Ext JS Library 1.1.1
26354  * Copyright(c) 2006-2007, Ext JS, LLC.
26355  * Licence LGPL
26356  * 
26357  */
26358  
26359  
26360 Roo.form.HtmlEditor = function(config){
26361     
26362     
26363     
26364     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26365     
26366     if (!this.toolbars) {
26367         this.toolbars = [];
26368     }
26369     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26370     
26371     
26372 };
26373
26374 /**
26375  * @class Roo.form.HtmlEditor
26376  * @extends Roo.form.Field
26377  * Provides a lightweight HTML Editor component.
26378  *
26379  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26380  * 
26381  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26382  * supported by this editor.</b><br/><br/>
26383  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26384  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26385  */
26386 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26387     /**
26388      * @cfg {Boolean} clearUp
26389      */
26390     clearUp : true,
26391       /**
26392      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26393      */
26394     toolbars : false,
26395    
26396      /**
26397      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26398      *                        Roo.resizable.
26399      */
26400     resizable : false,
26401      /**
26402      * @cfg {Number} height (in pixels)
26403      */   
26404     height: 300,
26405    /**
26406      * @cfg {Number} width (in pixels)
26407      */   
26408     width: 500,
26409     
26410     /**
26411      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26412      * 
26413      */
26414     stylesheets: false,
26415     
26416     
26417      /**
26418      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26419      * 
26420      */
26421     cblack: false,
26422     /**
26423      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26424      * 
26425      */
26426     cwhite: false,
26427     
26428      /**
26429      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26430      * 
26431      */
26432     black: false,
26433     /**
26434      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26435      * 
26436      */
26437     white: false,
26438     
26439     // id of frame..
26440     frameId: false,
26441     
26442     // private properties
26443     validationEvent : false,
26444     deferHeight: true,
26445     initialized : false,
26446     activated : false,
26447     
26448     onFocus : Roo.emptyFn,
26449     iframePad:3,
26450     hideMode:'offsets',
26451     
26452     actionMode : 'container', // defaults to hiding it...
26453     
26454     defaultAutoCreate : { // modified by initCompnoent..
26455         tag: "textarea",
26456         style:"width:500px;height:300px;",
26457         autocomplete: "new-password"
26458     },
26459
26460     // private
26461     initComponent : function(){
26462         this.addEvents({
26463             /**
26464              * @event initialize
26465              * Fires when the editor is fully initialized (including the iframe)
26466              * @param {HtmlEditor} this
26467              */
26468             initialize: true,
26469             /**
26470              * @event activate
26471              * Fires when the editor is first receives the focus. Any insertion must wait
26472              * until after this event.
26473              * @param {HtmlEditor} this
26474              */
26475             activate: true,
26476              /**
26477              * @event beforesync
26478              * Fires before the textarea is updated with content from the editor iframe. Return false
26479              * to cancel the sync.
26480              * @param {HtmlEditor} this
26481              * @param {String} html
26482              */
26483             beforesync: true,
26484              /**
26485              * @event beforepush
26486              * Fires before the iframe editor is updated with content from the textarea. Return false
26487              * to cancel the push.
26488              * @param {HtmlEditor} this
26489              * @param {String} html
26490              */
26491             beforepush: true,
26492              /**
26493              * @event sync
26494              * Fires when the textarea is updated with content from the editor iframe.
26495              * @param {HtmlEditor} this
26496              * @param {String} html
26497              */
26498             sync: true,
26499              /**
26500              * @event push
26501              * Fires when the iframe editor is updated with content from the textarea.
26502              * @param {HtmlEditor} this
26503              * @param {String} html
26504              */
26505             push: true,
26506              /**
26507              * @event editmodechange
26508              * Fires when the editor switches edit modes
26509              * @param {HtmlEditor} this
26510              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26511              */
26512             editmodechange: true,
26513             /**
26514              * @event editorevent
26515              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26516              * @param {HtmlEditor} this
26517              */
26518             editorevent: true,
26519             /**
26520              * @event firstfocus
26521              * Fires when on first focus - needed by toolbars..
26522              * @param {HtmlEditor} this
26523              */
26524             firstfocus: true,
26525             /**
26526              * @event autosave
26527              * Auto save the htmlEditor value as a file into Events
26528              * @param {HtmlEditor} this
26529              */
26530             autosave: true,
26531             /**
26532              * @event savedpreview
26533              * preview the saved version of htmlEditor
26534              * @param {HtmlEditor} this
26535              */
26536             savedpreview: true,
26537             
26538             /**
26539             * @event stylesheetsclick
26540             * Fires when press the Sytlesheets button
26541             * @param {Roo.HtmlEditorCore} this
26542             */
26543             stylesheetsclick: true
26544         });
26545         this.defaultAutoCreate =  {
26546             tag: "textarea",
26547             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26548             autocomplete: "new-password"
26549         };
26550     },
26551
26552     /**
26553      * Protected method that will not generally be called directly. It
26554      * is called when the editor creates its toolbar. Override this method if you need to
26555      * add custom toolbar buttons.
26556      * @param {HtmlEditor} editor
26557      */
26558     createToolbar : function(editor){
26559         Roo.log("create toolbars");
26560         if (!editor.toolbars || !editor.toolbars.length) {
26561             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26562         }
26563         
26564         for (var i =0 ; i < editor.toolbars.length;i++) {
26565             editor.toolbars[i] = Roo.factory(
26566                     typeof(editor.toolbars[i]) == 'string' ?
26567                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26568                 Roo.form.HtmlEditor);
26569             editor.toolbars[i].init(editor);
26570         }
26571          
26572         
26573     },
26574
26575      
26576     // private
26577     onRender : function(ct, position)
26578     {
26579         var _t = this;
26580         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26581         
26582         this.wrap = this.el.wrap({
26583             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26584         });
26585         
26586         this.editorcore.onRender(ct, position);
26587          
26588         if (this.resizable) {
26589             this.resizeEl = new Roo.Resizable(this.wrap, {
26590                 pinned : true,
26591                 wrap: true,
26592                 dynamic : true,
26593                 minHeight : this.height,
26594                 height: this.height,
26595                 handles : this.resizable,
26596                 width: this.width,
26597                 listeners : {
26598                     resize : function(r, w, h) {
26599                         _t.onResize(w,h); // -something
26600                     }
26601                 }
26602             });
26603             
26604         }
26605         this.createToolbar(this);
26606        
26607         
26608         if(!this.width){
26609             this.setSize(this.wrap.getSize());
26610         }
26611         if (this.resizeEl) {
26612             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26613             // should trigger onReize..
26614         }
26615         
26616         this.keyNav = new Roo.KeyNav(this.el, {
26617             
26618             "tab" : function(e){
26619                 e.preventDefault();
26620                 
26621                 var value = this.getValue();
26622                 
26623                 var start = this.el.dom.selectionStart;
26624                 var end = this.el.dom.selectionEnd;
26625                 
26626                 if(!e.shiftKey){
26627                     
26628                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26629                     this.el.dom.setSelectionRange(end + 1, end + 1);
26630                     return;
26631                 }
26632                 
26633                 var f = value.substring(0, start).split("\t");
26634                 
26635                 if(f.pop().length != 0){
26636                     return;
26637                 }
26638                 
26639                 this.setValue(f.join("\t") + value.substring(end));
26640                 this.el.dom.setSelectionRange(start - 1, start - 1);
26641                 
26642             },
26643             
26644             "home" : function(e){
26645                 e.preventDefault();
26646                 
26647                 var curr = this.el.dom.selectionStart;
26648                 var lines = this.getValue().split("\n");
26649                 
26650                 if(!lines.length){
26651                     return;
26652                 }
26653                 
26654                 if(e.ctrlKey){
26655                     this.el.dom.setSelectionRange(0, 0);
26656                     return;
26657                 }
26658                 
26659                 var pos = 0;
26660                 
26661                 for (var i = 0; i < lines.length;i++) {
26662                     pos += lines[i].length;
26663                     
26664                     if(i != 0){
26665                         pos += 1;
26666                     }
26667                     
26668                     if(pos < curr){
26669                         continue;
26670                     }
26671                     
26672                     pos -= lines[i].length;
26673                     
26674                     break;
26675                 }
26676                 
26677                 if(!e.shiftKey){
26678                     this.el.dom.setSelectionRange(pos, pos);
26679                     return;
26680                 }
26681                 
26682                 this.el.dom.selectionStart = pos;
26683                 this.el.dom.selectionEnd = curr;
26684             },
26685             
26686             "end" : function(e){
26687                 e.preventDefault();
26688                 
26689                 var curr = this.el.dom.selectionStart;
26690                 var lines = this.getValue().split("\n");
26691                 
26692                 if(!lines.length){
26693                     return;
26694                 }
26695                 
26696                 if(e.ctrlKey){
26697                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26698                     return;
26699                 }
26700                 
26701                 var pos = 0;
26702                 
26703                 for (var i = 0; i < lines.length;i++) {
26704                     
26705                     pos += lines[i].length;
26706                     
26707                     if(i != 0){
26708                         pos += 1;
26709                     }
26710                     
26711                     if(pos < curr){
26712                         continue;
26713                     }
26714                     
26715                     break;
26716                 }
26717                 
26718                 if(!e.shiftKey){
26719                     this.el.dom.setSelectionRange(pos, pos);
26720                     return;
26721                 }
26722                 
26723                 this.el.dom.selectionStart = curr;
26724                 this.el.dom.selectionEnd = pos;
26725             },
26726
26727             scope : this,
26728
26729             doRelay : function(foo, bar, hname){
26730                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26731             },
26732
26733             forceKeyDown: true
26734         });
26735         
26736 //        if(this.autosave && this.w){
26737 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26738 //        }
26739     },
26740
26741     // private
26742     onResize : function(w, h)
26743     {
26744         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26745         var ew = false;
26746         var eh = false;
26747         
26748         if(this.el ){
26749             if(typeof w == 'number'){
26750                 var aw = w - this.wrap.getFrameWidth('lr');
26751                 this.el.setWidth(this.adjustWidth('textarea', aw));
26752                 ew = aw;
26753             }
26754             if(typeof h == 'number'){
26755                 var tbh = 0;
26756                 for (var i =0; i < this.toolbars.length;i++) {
26757                     // fixme - ask toolbars for heights?
26758                     tbh += this.toolbars[i].tb.el.getHeight();
26759                     if (this.toolbars[i].footer) {
26760                         tbh += this.toolbars[i].footer.el.getHeight();
26761                     }
26762                 }
26763                 
26764                 
26765                 
26766                 
26767                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26768                 ah -= 5; // knock a few pixes off for look..
26769 //                Roo.log(ah);
26770                 this.el.setHeight(this.adjustWidth('textarea', ah));
26771                 var eh = ah;
26772             }
26773         }
26774         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26775         this.editorcore.onResize(ew,eh);
26776         
26777     },
26778
26779     /**
26780      * Toggles the editor between standard and source edit mode.
26781      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26782      */
26783     toggleSourceEdit : function(sourceEditMode)
26784     {
26785         this.editorcore.toggleSourceEdit(sourceEditMode);
26786         
26787         if(this.editorcore.sourceEditMode){
26788             Roo.log('editor - showing textarea');
26789             
26790 //            Roo.log('in');
26791 //            Roo.log(this.syncValue());
26792             this.editorcore.syncValue();
26793             this.el.removeClass('x-hidden');
26794             this.el.dom.removeAttribute('tabIndex');
26795             this.el.focus();
26796             
26797             for (var i = 0; i < this.toolbars.length; i++) {
26798                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26799                     this.toolbars[i].tb.hide();
26800                     this.toolbars[i].footer.hide();
26801                 }
26802             }
26803             
26804         }else{
26805             Roo.log('editor - hiding textarea');
26806 //            Roo.log('out')
26807 //            Roo.log(this.pushValue()); 
26808             this.editorcore.pushValue();
26809             
26810             this.el.addClass('x-hidden');
26811             this.el.dom.setAttribute('tabIndex', -1);
26812             
26813             for (var i = 0; i < this.toolbars.length; i++) {
26814                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26815                     this.toolbars[i].tb.show();
26816                     this.toolbars[i].footer.show();
26817                 }
26818             }
26819             
26820             //this.deferFocus();
26821         }
26822         
26823         this.setSize(this.wrap.getSize());
26824         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26825         
26826         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26827     },
26828  
26829     // private (for BoxComponent)
26830     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26831
26832     // private (for BoxComponent)
26833     getResizeEl : function(){
26834         return this.wrap;
26835     },
26836
26837     // private (for BoxComponent)
26838     getPositionEl : function(){
26839         return this.wrap;
26840     },
26841
26842     // private
26843     initEvents : function(){
26844         this.originalValue = this.getValue();
26845     },
26846
26847     /**
26848      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26849      * @method
26850      */
26851     markInvalid : Roo.emptyFn,
26852     /**
26853      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26854      * @method
26855      */
26856     clearInvalid : Roo.emptyFn,
26857
26858     setValue : function(v){
26859         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26860         this.editorcore.pushValue();
26861     },
26862
26863      
26864     // private
26865     deferFocus : function(){
26866         this.focus.defer(10, this);
26867     },
26868
26869     // doc'ed in Field
26870     focus : function(){
26871         this.editorcore.focus();
26872         
26873     },
26874       
26875
26876     // private
26877     onDestroy : function(){
26878         
26879         
26880         
26881         if(this.rendered){
26882             
26883             for (var i =0; i < this.toolbars.length;i++) {
26884                 // fixme - ask toolbars for heights?
26885                 this.toolbars[i].onDestroy();
26886             }
26887             
26888             this.wrap.dom.innerHTML = '';
26889             this.wrap.remove();
26890         }
26891     },
26892
26893     // private
26894     onFirstFocus : function(){
26895         //Roo.log("onFirstFocus");
26896         this.editorcore.onFirstFocus();
26897          for (var i =0; i < this.toolbars.length;i++) {
26898             this.toolbars[i].onFirstFocus();
26899         }
26900         
26901     },
26902     
26903     // private
26904     syncValue : function()
26905     {
26906         this.editorcore.syncValue();
26907     },
26908     
26909     pushValue : function()
26910     {
26911         this.editorcore.pushValue();
26912     },
26913     
26914     setStylesheets : function(stylesheets)
26915     {
26916         this.editorcore.setStylesheets(stylesheets);
26917     },
26918     
26919     removeStylesheets : function()
26920     {
26921         this.editorcore.removeStylesheets();
26922     }
26923      
26924     
26925     // hide stuff that is not compatible
26926     /**
26927      * @event blur
26928      * @hide
26929      */
26930     /**
26931      * @event change
26932      * @hide
26933      */
26934     /**
26935      * @event focus
26936      * @hide
26937      */
26938     /**
26939      * @event specialkey
26940      * @hide
26941      */
26942     /**
26943      * @cfg {String} fieldClass @hide
26944      */
26945     /**
26946      * @cfg {String} focusClass @hide
26947      */
26948     /**
26949      * @cfg {String} autoCreate @hide
26950      */
26951     /**
26952      * @cfg {String} inputType @hide
26953      */
26954     /**
26955      * @cfg {String} invalidClass @hide
26956      */
26957     /**
26958      * @cfg {String} invalidText @hide
26959      */
26960     /**
26961      * @cfg {String} msgFx @hide
26962      */
26963     /**
26964      * @cfg {String} validateOnBlur @hide
26965      */
26966 });
26967  
26968     // <script type="text/javascript">
26969 /*
26970  * Based on
26971  * Ext JS Library 1.1.1
26972  * Copyright(c) 2006-2007, Ext JS, LLC.
26973  *  
26974  
26975  */
26976
26977 /**
26978  * @class Roo.form.HtmlEditorToolbar1
26979  * Basic Toolbar
26980  * 
26981  * Usage:
26982  *
26983  new Roo.form.HtmlEditor({
26984     ....
26985     toolbars : [
26986         new Roo.form.HtmlEditorToolbar1({
26987             disable : { fonts: 1 , format: 1, ..., ... , ...],
26988             btns : [ .... ]
26989         })
26990     }
26991      
26992  * 
26993  * @cfg {Object} disable List of elements to disable..
26994  * @cfg {Array} btns List of additional buttons.
26995  * 
26996  * 
26997  * NEEDS Extra CSS? 
26998  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26999  */
27000  
27001 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27002 {
27003     
27004     Roo.apply(this, config);
27005     
27006     // default disabled, based on 'good practice'..
27007     this.disable = this.disable || {};
27008     Roo.applyIf(this.disable, {
27009         fontSize : true,
27010         colors : true,
27011         specialElements : true
27012     });
27013     
27014     
27015     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27016     // dont call parent... till later.
27017 }
27018
27019 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
27020     
27021     tb: false,
27022     
27023     rendered: false,
27024     
27025     editor : false,
27026     editorcore : false,
27027     /**
27028      * @cfg {Object} disable  List of toolbar elements to disable
27029          
27030      */
27031     disable : false,
27032     
27033     
27034      /**
27035      * @cfg {String} createLinkText The default text for the create link prompt
27036      */
27037     createLinkText : 'Please enter the URL for the link:',
27038     /**
27039      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27040      */
27041     defaultLinkValue : 'http:/'+'/',
27042    
27043     
27044       /**
27045      * @cfg {Array} fontFamilies An array of available font families
27046      */
27047     fontFamilies : [
27048         'Arial',
27049         'Courier New',
27050         'Tahoma',
27051         'Times New Roman',
27052         'Verdana'
27053     ],
27054     
27055     specialChars : [
27056            "&#169;",
27057           "&#174;",     
27058           "&#8482;",    
27059           "&#163;" ,    
27060          // "&#8212;",    
27061           "&#8230;",    
27062           "&#247;" ,    
27063         //  "&#225;" ,     ?? a acute?
27064            "&#8364;"    , //Euro
27065        //   "&#8220;"    ,
27066         //  "&#8221;"    ,
27067         //  "&#8226;"    ,
27068           "&#176;"  //   , // degrees
27069
27070          // "&#233;"     , // e ecute
27071          // "&#250;"     , // u ecute?
27072     ],
27073     
27074     specialElements : [
27075         {
27076             text: "Insert Table",
27077             xtype: 'MenuItem',
27078             xns : Roo.Menu,
27079             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27080                 
27081         },
27082         {    
27083             text: "Insert Image",
27084             xtype: 'MenuItem',
27085             xns : Roo.Menu,
27086             ihtml : '<img src="about:blank"/>'
27087             
27088         }
27089         
27090          
27091     ],
27092     
27093     
27094     inputElements : [ 
27095             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27096             "input:submit", "input:button", "select", "textarea", "label" ],
27097     formats : [
27098         ["p"] ,  
27099         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27100         ["pre"],[ "code"], 
27101         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27102         ['div'],['span']
27103     ],
27104     
27105     cleanStyles : [
27106         "font-size"
27107     ],
27108      /**
27109      * @cfg {String} defaultFont default font to use.
27110      */
27111     defaultFont: 'tahoma',
27112    
27113     fontSelect : false,
27114     
27115     
27116     formatCombo : false,
27117     
27118     init : function(editor)
27119     {
27120         this.editor = editor;
27121         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27122         var editorcore = this.editorcore;
27123         
27124         var _t = this;
27125         
27126         var fid = editorcore.frameId;
27127         var etb = this;
27128         function btn(id, toggle, handler){
27129             var xid = fid + '-'+ id ;
27130             return {
27131                 id : xid,
27132                 cmd : id,
27133                 cls : 'x-btn-icon x-edit-'+id,
27134                 enableToggle:toggle !== false,
27135                 scope: _t, // was editor...
27136                 handler:handler||_t.relayBtnCmd,
27137                 clickEvent:'mousedown',
27138                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27139                 tabIndex:-1
27140             };
27141         }
27142         
27143         
27144         
27145         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27146         this.tb = tb;
27147          // stop form submits
27148         tb.el.on('click', function(e){
27149             e.preventDefault(); // what does this do?
27150         });
27151
27152         if(!this.disable.font) { // && !Roo.isSafari){
27153             /* why no safari for fonts 
27154             editor.fontSelect = tb.el.createChild({
27155                 tag:'select',
27156                 tabIndex: -1,
27157                 cls:'x-font-select',
27158                 html: this.createFontOptions()
27159             });
27160             
27161             editor.fontSelect.on('change', function(){
27162                 var font = editor.fontSelect.dom.value;
27163                 editor.relayCmd('fontname', font);
27164                 editor.deferFocus();
27165             }, editor);
27166             
27167             tb.add(
27168                 editor.fontSelect.dom,
27169                 '-'
27170             );
27171             */
27172             
27173         };
27174         if(!this.disable.formats){
27175             this.formatCombo = new Roo.form.ComboBox({
27176                 store: new Roo.data.SimpleStore({
27177                     id : 'tag',
27178                     fields: ['tag'],
27179                     data : this.formats // from states.js
27180                 }),
27181                 blockFocus : true,
27182                 name : '',
27183                 //autoCreate : {tag: "div",  size: "20"},
27184                 displayField:'tag',
27185                 typeAhead: false,
27186                 mode: 'local',
27187                 editable : false,
27188                 triggerAction: 'all',
27189                 emptyText:'Add tag',
27190                 selectOnFocus:true,
27191                 width:135,
27192                 listeners : {
27193                     'select': function(c, r, i) {
27194                         editorcore.insertTag(r.get('tag'));
27195                         editor.focus();
27196                     }
27197                 }
27198
27199             });
27200             tb.addField(this.formatCombo);
27201             
27202         }
27203         
27204         if(!this.disable.format){
27205             tb.add(
27206                 btn('bold'),
27207                 btn('italic'),
27208                 btn('underline'),
27209                 btn('strikethrough')
27210             );
27211         };
27212         if(!this.disable.fontSize){
27213             tb.add(
27214                 '-',
27215                 
27216                 
27217                 btn('increasefontsize', false, editorcore.adjustFont),
27218                 btn('decreasefontsize', false, editorcore.adjustFont)
27219             );
27220         };
27221         
27222         
27223         if(!this.disable.colors){
27224             tb.add(
27225                 '-', {
27226                     id:editorcore.frameId +'-forecolor',
27227                     cls:'x-btn-icon x-edit-forecolor',
27228                     clickEvent:'mousedown',
27229                     tooltip: this.buttonTips['forecolor'] || undefined,
27230                     tabIndex:-1,
27231                     menu : new Roo.menu.ColorMenu({
27232                         allowReselect: true,
27233                         focus: Roo.emptyFn,
27234                         value:'000000',
27235                         plain:true,
27236                         selectHandler: function(cp, color){
27237                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27238                             editor.deferFocus();
27239                         },
27240                         scope: editorcore,
27241                         clickEvent:'mousedown'
27242                     })
27243                 }, {
27244                     id:editorcore.frameId +'backcolor',
27245                     cls:'x-btn-icon x-edit-backcolor',
27246                     clickEvent:'mousedown',
27247                     tooltip: this.buttonTips['backcolor'] || undefined,
27248                     tabIndex:-1,
27249                     menu : new Roo.menu.ColorMenu({
27250                         focus: Roo.emptyFn,
27251                         value:'FFFFFF',
27252                         plain:true,
27253                         allowReselect: true,
27254                         selectHandler: function(cp, color){
27255                             if(Roo.isGecko){
27256                                 editorcore.execCmd('useCSS', false);
27257                                 editorcore.execCmd('hilitecolor', color);
27258                                 editorcore.execCmd('useCSS', true);
27259                                 editor.deferFocus();
27260                             }else{
27261                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27262                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27263                                 editor.deferFocus();
27264                             }
27265                         },
27266                         scope:editorcore,
27267                         clickEvent:'mousedown'
27268                     })
27269                 }
27270             );
27271         };
27272         // now add all the items...
27273         
27274
27275         if(!this.disable.alignments){
27276             tb.add(
27277                 '-',
27278                 btn('justifyleft'),
27279                 btn('justifycenter'),
27280                 btn('justifyright')
27281             );
27282         };
27283
27284         //if(!Roo.isSafari){
27285             if(!this.disable.links){
27286                 tb.add(
27287                     '-',
27288                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27289                 );
27290             };
27291
27292             if(!this.disable.lists){
27293                 tb.add(
27294                     '-',
27295                     btn('insertorderedlist'),
27296                     btn('insertunorderedlist')
27297                 );
27298             }
27299             if(!this.disable.sourceEdit){
27300                 tb.add(
27301                     '-',
27302                     btn('sourceedit', true, function(btn){
27303                         this.toggleSourceEdit(btn.pressed);
27304                     })
27305                 );
27306             }
27307         //}
27308         
27309         var smenu = { };
27310         // special menu.. - needs to be tidied up..
27311         if (!this.disable.special) {
27312             smenu = {
27313                 text: "&#169;",
27314                 cls: 'x-edit-none',
27315                 
27316                 menu : {
27317                     items : []
27318                 }
27319             };
27320             for (var i =0; i < this.specialChars.length; i++) {
27321                 smenu.menu.items.push({
27322                     
27323                     html: this.specialChars[i],
27324                     handler: function(a,b) {
27325                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27326                         //editor.insertAtCursor(a.html);
27327                         
27328                     },
27329                     tabIndex:-1
27330                 });
27331             }
27332             
27333             
27334             tb.add(smenu);
27335             
27336             
27337         }
27338         
27339         var cmenu = { };
27340         if (!this.disable.cleanStyles) {
27341             cmenu = {
27342                 cls: 'x-btn-icon x-btn-clear',
27343                 
27344                 menu : {
27345                     items : []
27346                 }
27347             };
27348             for (var i =0; i < this.cleanStyles.length; i++) {
27349                 cmenu.menu.items.push({
27350                     actiontype : this.cleanStyles[i],
27351                     html: 'Remove ' + this.cleanStyles[i],
27352                     handler: function(a,b) {
27353 //                        Roo.log(a);
27354 //                        Roo.log(b);
27355                         var c = Roo.get(editorcore.doc.body);
27356                         c.select('[style]').each(function(s) {
27357                             s.dom.style.removeProperty(a.actiontype);
27358                         });
27359                         editorcore.syncValue();
27360                     },
27361                     tabIndex:-1
27362                 });
27363             }
27364              cmenu.menu.items.push({
27365                 actiontype : 'tablewidths',
27366                 html: 'Remove Table Widths',
27367                 handler: function(a,b) {
27368                     editorcore.cleanTableWidths();
27369                     editorcore.syncValue();
27370                 },
27371                 tabIndex:-1
27372             });
27373             cmenu.menu.items.push({
27374                 actiontype : 'word',
27375                 html: 'Remove MS Word Formating',
27376                 handler: function(a,b) {
27377                     editorcore.cleanWord();
27378                     editorcore.syncValue();
27379                 },
27380                 tabIndex:-1
27381             });
27382             
27383             cmenu.menu.items.push({
27384                 actiontype : 'all',
27385                 html: 'Remove All Styles',
27386                 handler: function(a,b) {
27387                     
27388                     var c = Roo.get(editorcore.doc.body);
27389                     c.select('[style]').each(function(s) {
27390                         s.dom.removeAttribute('style');
27391                     });
27392                     editorcore.syncValue();
27393                 },
27394                 tabIndex:-1
27395             });
27396             
27397             cmenu.menu.items.push({
27398                 actiontype : 'all',
27399                 html: 'Remove All CSS Classes',
27400                 handler: function(a,b) {
27401                     
27402                     var c = Roo.get(editorcore.doc.body);
27403                     c.select('[class]').each(function(s) {
27404                         s.dom.className = '';
27405                     });
27406                     editorcore.syncValue();
27407                 },
27408                 tabIndex:-1
27409             });
27410             
27411              cmenu.menu.items.push({
27412                 actiontype : 'tidy',
27413                 html: 'Tidy HTML Source',
27414                 handler: function(a,b) {
27415                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27416                     editorcore.syncValue();
27417                 },
27418                 tabIndex:-1
27419             });
27420             
27421             
27422             tb.add(cmenu);
27423         }
27424          
27425         if (!this.disable.specialElements) {
27426             var semenu = {
27427                 text: "Other;",
27428                 cls: 'x-edit-none',
27429                 menu : {
27430                     items : []
27431                 }
27432             };
27433             for (var i =0; i < this.specialElements.length; i++) {
27434                 semenu.menu.items.push(
27435                     Roo.apply({ 
27436                         handler: function(a,b) {
27437                             editor.insertAtCursor(this.ihtml);
27438                         }
27439                     }, this.specialElements[i])
27440                 );
27441                     
27442             }
27443             
27444             tb.add(semenu);
27445             
27446             
27447         }
27448          
27449         
27450         if (this.btns) {
27451             for(var i =0; i< this.btns.length;i++) {
27452                 var b = Roo.factory(this.btns[i],Roo.form);
27453                 b.cls =  'x-edit-none';
27454                 
27455                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27456                     b.cls += ' x-init-enable';
27457                 }
27458                 
27459                 b.scope = editorcore;
27460                 tb.add(b);
27461             }
27462         
27463         }
27464         
27465         
27466         
27467         // disable everything...
27468         
27469         this.tb.items.each(function(item){
27470             
27471            if(
27472                 item.id != editorcore.frameId+ '-sourceedit' && 
27473                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27474             ){
27475                 
27476                 item.disable();
27477             }
27478         });
27479         this.rendered = true;
27480         
27481         // the all the btns;
27482         editor.on('editorevent', this.updateToolbar, this);
27483         // other toolbars need to implement this..
27484         //editor.on('editmodechange', this.updateToolbar, this);
27485     },
27486     
27487     
27488     relayBtnCmd : function(btn) {
27489         this.editorcore.relayCmd(btn.cmd);
27490     },
27491     // private used internally
27492     createLink : function(){
27493         Roo.log("create link?");
27494         var url = prompt(this.createLinkText, this.defaultLinkValue);
27495         if(url && url != 'http:/'+'/'){
27496             this.editorcore.relayCmd('createlink', url);
27497         }
27498     },
27499
27500     
27501     /**
27502      * Protected method that will not generally be called directly. It triggers
27503      * a toolbar update by reading the markup state of the current selection in the editor.
27504      */
27505     updateToolbar: function(){
27506
27507         if(!this.editorcore.activated){
27508             this.editor.onFirstFocus();
27509             return;
27510         }
27511
27512         var btns = this.tb.items.map, 
27513             doc = this.editorcore.doc,
27514             frameId = this.editorcore.frameId;
27515
27516         if(!this.disable.font && !Roo.isSafari){
27517             /*
27518             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27519             if(name != this.fontSelect.dom.value){
27520                 this.fontSelect.dom.value = name;
27521             }
27522             */
27523         }
27524         if(!this.disable.format){
27525             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27526             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27527             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27528             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27529         }
27530         if(!this.disable.alignments){
27531             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27532             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27533             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27534         }
27535         if(!Roo.isSafari && !this.disable.lists){
27536             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27537             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27538         }
27539         
27540         var ans = this.editorcore.getAllAncestors();
27541         if (this.formatCombo) {
27542             
27543             
27544             var store = this.formatCombo.store;
27545             this.formatCombo.setValue("");
27546             for (var i =0; i < ans.length;i++) {
27547                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27548                     // select it..
27549                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27550                     break;
27551                 }
27552             }
27553         }
27554         
27555         
27556         
27557         // hides menus... - so this cant be on a menu...
27558         Roo.menu.MenuMgr.hideAll();
27559
27560         //this.editorsyncValue();
27561     },
27562    
27563     
27564     createFontOptions : function(){
27565         var buf = [], fs = this.fontFamilies, ff, lc;
27566         
27567         
27568         
27569         for(var i = 0, len = fs.length; i< len; i++){
27570             ff = fs[i];
27571             lc = ff.toLowerCase();
27572             buf.push(
27573                 '<option value="',lc,'" style="font-family:',ff,';"',
27574                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27575                     ff,
27576                 '</option>'
27577             );
27578         }
27579         return buf.join('');
27580     },
27581     
27582     toggleSourceEdit : function(sourceEditMode){
27583         
27584         Roo.log("toolbar toogle");
27585         if(sourceEditMode === undefined){
27586             sourceEditMode = !this.sourceEditMode;
27587         }
27588         this.sourceEditMode = sourceEditMode === true;
27589         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27590         // just toggle the button?
27591         if(btn.pressed !== this.sourceEditMode){
27592             btn.toggle(this.sourceEditMode);
27593             return;
27594         }
27595         
27596         if(sourceEditMode){
27597             Roo.log("disabling buttons");
27598             this.tb.items.each(function(item){
27599                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27600                     item.disable();
27601                 }
27602             });
27603           
27604         }else{
27605             Roo.log("enabling buttons");
27606             if(this.editorcore.initialized){
27607                 this.tb.items.each(function(item){
27608                     item.enable();
27609                 });
27610             }
27611             
27612         }
27613         Roo.log("calling toggole on editor");
27614         // tell the editor that it's been pressed..
27615         this.editor.toggleSourceEdit(sourceEditMode);
27616        
27617     },
27618      /**
27619      * Object collection of toolbar tooltips for the buttons in the editor. The key
27620      * is the command id associated with that button and the value is a valid QuickTips object.
27621      * For example:
27622 <pre><code>
27623 {
27624     bold : {
27625         title: 'Bold (Ctrl+B)',
27626         text: 'Make the selected text bold.',
27627         cls: 'x-html-editor-tip'
27628     },
27629     italic : {
27630         title: 'Italic (Ctrl+I)',
27631         text: 'Make the selected text italic.',
27632         cls: 'x-html-editor-tip'
27633     },
27634     ...
27635 </code></pre>
27636     * @type Object
27637      */
27638     buttonTips : {
27639         bold : {
27640             title: 'Bold (Ctrl+B)',
27641             text: 'Make the selected text bold.',
27642             cls: 'x-html-editor-tip'
27643         },
27644         italic : {
27645             title: 'Italic (Ctrl+I)',
27646             text: 'Make the selected text italic.',
27647             cls: 'x-html-editor-tip'
27648         },
27649         underline : {
27650             title: 'Underline (Ctrl+U)',
27651             text: 'Underline the selected text.',
27652             cls: 'x-html-editor-tip'
27653         },
27654         strikethrough : {
27655             title: 'Strikethrough',
27656             text: 'Strikethrough the selected text.',
27657             cls: 'x-html-editor-tip'
27658         },
27659         increasefontsize : {
27660             title: 'Grow Text',
27661             text: 'Increase the font size.',
27662             cls: 'x-html-editor-tip'
27663         },
27664         decreasefontsize : {
27665             title: 'Shrink Text',
27666             text: 'Decrease the font size.',
27667             cls: 'x-html-editor-tip'
27668         },
27669         backcolor : {
27670             title: 'Text Highlight Color',
27671             text: 'Change the background color of the selected text.',
27672             cls: 'x-html-editor-tip'
27673         },
27674         forecolor : {
27675             title: 'Font Color',
27676             text: 'Change the color of the selected text.',
27677             cls: 'x-html-editor-tip'
27678         },
27679         justifyleft : {
27680             title: 'Align Text Left',
27681             text: 'Align text to the left.',
27682             cls: 'x-html-editor-tip'
27683         },
27684         justifycenter : {
27685             title: 'Center Text',
27686             text: 'Center text in the editor.',
27687             cls: 'x-html-editor-tip'
27688         },
27689         justifyright : {
27690             title: 'Align Text Right',
27691             text: 'Align text to the right.',
27692             cls: 'x-html-editor-tip'
27693         },
27694         insertunorderedlist : {
27695             title: 'Bullet List',
27696             text: 'Start a bulleted list.',
27697             cls: 'x-html-editor-tip'
27698         },
27699         insertorderedlist : {
27700             title: 'Numbered List',
27701             text: 'Start a numbered list.',
27702             cls: 'x-html-editor-tip'
27703         },
27704         createlink : {
27705             title: 'Hyperlink',
27706             text: 'Make the selected text a hyperlink.',
27707             cls: 'x-html-editor-tip'
27708         },
27709         sourceedit : {
27710             title: 'Source Edit',
27711             text: 'Switch to source editing mode.',
27712             cls: 'x-html-editor-tip'
27713         }
27714     },
27715     // private
27716     onDestroy : function(){
27717         if(this.rendered){
27718             
27719             this.tb.items.each(function(item){
27720                 if(item.menu){
27721                     item.menu.removeAll();
27722                     if(item.menu.el){
27723                         item.menu.el.destroy();
27724                     }
27725                 }
27726                 item.destroy();
27727             });
27728              
27729         }
27730     },
27731     onFirstFocus: function() {
27732         this.tb.items.each(function(item){
27733            item.enable();
27734         });
27735     }
27736 });
27737
27738
27739
27740
27741 // <script type="text/javascript">
27742 /*
27743  * Based on
27744  * Ext JS Library 1.1.1
27745  * Copyright(c) 2006-2007, Ext JS, LLC.
27746  *  
27747  
27748  */
27749
27750  
27751 /**
27752  * @class Roo.form.HtmlEditor.ToolbarContext
27753  * Context Toolbar
27754  * 
27755  * Usage:
27756  *
27757  new Roo.form.HtmlEditor({
27758     ....
27759     toolbars : [
27760         { xtype: 'ToolbarStandard', styles : {} }
27761         { xtype: 'ToolbarContext', disable : {} }
27762     ]
27763 })
27764
27765      
27766  * 
27767  * @config : {Object} disable List of elements to disable.. (not done yet.)
27768  * @config : {Object} styles  Map of styles available.
27769  * 
27770  */
27771
27772 Roo.form.HtmlEditor.ToolbarContext = function(config)
27773 {
27774     
27775     Roo.apply(this, config);
27776     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27777     // dont call parent... till later.
27778     this.styles = this.styles || {};
27779 }
27780
27781  
27782
27783 Roo.form.HtmlEditor.ToolbarContext.types = {
27784     'IMG' : {
27785         width : {
27786             title: "Width",
27787             width: 40
27788         },
27789         height:  {
27790             title: "Height",
27791             width: 40
27792         },
27793         align: {
27794             title: "Align",
27795             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27796             width : 80
27797             
27798         },
27799         border: {
27800             title: "Border",
27801             width: 40
27802         },
27803         alt: {
27804             title: "Alt",
27805             width: 120
27806         },
27807         src : {
27808             title: "Src",
27809             width: 220
27810         }
27811         
27812     },
27813     'A' : {
27814         name : {
27815             title: "Name",
27816             width: 50
27817         },
27818         target:  {
27819             title: "Target",
27820             width: 120
27821         },
27822         href:  {
27823             title: "Href",
27824             width: 220
27825         } // border?
27826         
27827     },
27828     'TABLE' : {
27829         rows : {
27830             title: "Rows",
27831             width: 20
27832         },
27833         cols : {
27834             title: "Cols",
27835             width: 20
27836         },
27837         width : {
27838             title: "Width",
27839             width: 40
27840         },
27841         height : {
27842             title: "Height",
27843             width: 40
27844         },
27845         border : {
27846             title: "Border",
27847             width: 20
27848         }
27849     },
27850     'TD' : {
27851         width : {
27852             title: "Width",
27853             width: 40
27854         },
27855         height : {
27856             title: "Height",
27857             width: 40
27858         },   
27859         align: {
27860             title: "Align",
27861             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27862             width: 80
27863         },
27864         valign: {
27865             title: "Valign",
27866             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27867             width: 80
27868         },
27869         colspan: {
27870             title: "Colspan",
27871             width: 20
27872             
27873         },
27874          'font-family'  : {
27875             title : "Font",
27876             style : 'fontFamily',
27877             displayField: 'display',
27878             optname : 'font-family',
27879             width: 140
27880         }
27881     },
27882     'INPUT' : {
27883         name : {
27884             title: "name",
27885             width: 120
27886         },
27887         value : {
27888             title: "Value",
27889             width: 120
27890         },
27891         width : {
27892             title: "Width",
27893             width: 40
27894         }
27895     },
27896     'LABEL' : {
27897         'for' : {
27898             title: "For",
27899             width: 120
27900         }
27901     },
27902     'TEXTAREA' : {
27903           name : {
27904             title: "name",
27905             width: 120
27906         },
27907         rows : {
27908             title: "Rows",
27909             width: 20
27910         },
27911         cols : {
27912             title: "Cols",
27913             width: 20
27914         }
27915     },
27916     'SELECT' : {
27917         name : {
27918             title: "name",
27919             width: 120
27920         },
27921         selectoptions : {
27922             title: "Options",
27923             width: 200
27924         }
27925     },
27926     
27927     // should we really allow this??
27928     // should this just be 
27929     'BODY' : {
27930         title : {
27931             title: "Title",
27932             width: 200,
27933             disabled : true
27934         }
27935     },
27936     'SPAN' : {
27937         'font-family'  : {
27938             title : "Font",
27939             style : 'fontFamily',
27940             displayField: 'display',
27941             optname : 'font-family',
27942             width: 140
27943         }
27944     },
27945     'DIV' : {
27946         'font-family'  : {
27947             title : "Font",
27948             style : 'fontFamily',
27949             displayField: 'display',
27950             optname : 'font-family',
27951             width: 140
27952         }
27953     },
27954      'P' : {
27955         'font-family'  : {
27956             title : "Font",
27957             style : 'fontFamily',
27958             displayField: 'display',
27959             optname : 'font-family',
27960             width: 140
27961         }
27962     },
27963     
27964     '*' : {
27965         // empty..
27966     }
27967
27968 };
27969
27970 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27971 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27972
27973 Roo.form.HtmlEditor.ToolbarContext.options = {
27974         'font-family'  : [ 
27975                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27976                 [ 'Courier New', 'Courier New'],
27977                 [ 'Tahoma', 'Tahoma'],
27978                 [ 'Times New Roman,serif', 'Times'],
27979                 [ 'Verdana','Verdana' ]
27980         ]
27981 };
27982
27983 // fixme - these need to be configurable..
27984  
27985
27986 //Roo.form.HtmlEditor.ToolbarContext.types
27987
27988
27989 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27990     
27991     tb: false,
27992     
27993     rendered: false,
27994     
27995     editor : false,
27996     editorcore : false,
27997     /**
27998      * @cfg {Object} disable  List of toolbar elements to disable
27999          
28000      */
28001     disable : false,
28002     /**
28003      * @cfg {Object} styles List of styles 
28004      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28005      *
28006      * These must be defined in the page, so they get rendered correctly..
28007      * .headline { }
28008      * TD.underline { }
28009      * 
28010      */
28011     styles : false,
28012     
28013     options: false,
28014     
28015     toolbars : false,
28016     
28017     init : function(editor)
28018     {
28019         this.editor = editor;
28020         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28021         var editorcore = this.editorcore;
28022         
28023         var fid = editorcore.frameId;
28024         var etb = this;
28025         function btn(id, toggle, handler){
28026             var xid = fid + '-'+ id ;
28027             return {
28028                 id : xid,
28029                 cmd : id,
28030                 cls : 'x-btn-icon x-edit-'+id,
28031                 enableToggle:toggle !== false,
28032                 scope: editorcore, // was editor...
28033                 handler:handler||editorcore.relayBtnCmd,
28034                 clickEvent:'mousedown',
28035                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28036                 tabIndex:-1
28037             };
28038         }
28039         // create a new element.
28040         var wdiv = editor.wrap.createChild({
28041                 tag: 'div'
28042             }, editor.wrap.dom.firstChild.nextSibling, true);
28043         
28044         // can we do this more than once??
28045         
28046          // stop form submits
28047       
28048  
28049         // disable everything...
28050         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28051         this.toolbars = {};
28052            
28053         for (var i in  ty) {
28054           
28055             this.toolbars[i] = this.buildToolbar(ty[i],i);
28056         }
28057         this.tb = this.toolbars.BODY;
28058         this.tb.el.show();
28059         this.buildFooter();
28060         this.footer.show();
28061         editor.on('hide', function( ) { this.footer.hide() }, this);
28062         editor.on('show', function( ) { this.footer.show() }, this);
28063         
28064          
28065         this.rendered = true;
28066         
28067         // the all the btns;
28068         editor.on('editorevent', this.updateToolbar, this);
28069         // other toolbars need to implement this..
28070         //editor.on('editmodechange', this.updateToolbar, this);
28071     },
28072     
28073     
28074     
28075     /**
28076      * Protected method that will not generally be called directly. It triggers
28077      * a toolbar update by reading the markup state of the current selection in the editor.
28078      *
28079      * Note you can force an update by calling on('editorevent', scope, false)
28080      */
28081     updateToolbar: function(editor,ev,sel){
28082
28083         //Roo.log(ev);
28084         // capture mouse up - this is handy for selecting images..
28085         // perhaps should go somewhere else...
28086         if(!this.editorcore.activated){
28087              this.editor.onFirstFocus();
28088             return;
28089         }
28090         
28091         
28092         
28093         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28094         // selectNode - might want to handle IE?
28095         if (ev &&
28096             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28097             ev.target && ev.target.tagName == 'IMG') {
28098             // they have click on an image...
28099             // let's see if we can change the selection...
28100             sel = ev.target;
28101          
28102               var nodeRange = sel.ownerDocument.createRange();
28103             try {
28104                 nodeRange.selectNode(sel);
28105             } catch (e) {
28106                 nodeRange.selectNodeContents(sel);
28107             }
28108             //nodeRange.collapse(true);
28109             var s = this.editorcore.win.getSelection();
28110             s.removeAllRanges();
28111             s.addRange(nodeRange);
28112         }  
28113         
28114       
28115         var updateFooter = sel ? false : true;
28116         
28117         
28118         var ans = this.editorcore.getAllAncestors();
28119         
28120         // pick
28121         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28122         
28123         if (!sel) { 
28124             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28125             sel = sel ? sel : this.editorcore.doc.body;
28126             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28127             
28128         }
28129         // pick a menu that exists..
28130         var tn = sel.tagName.toUpperCase();
28131         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28132         
28133         tn = sel.tagName.toUpperCase();
28134         
28135         var lastSel = this.tb.selectedNode;
28136         
28137         this.tb.selectedNode = sel;
28138         
28139         // if current menu does not match..
28140         
28141         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28142                 
28143             this.tb.el.hide();
28144             ///console.log("show: " + tn);
28145             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28146             this.tb.el.show();
28147             // update name
28148             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28149             
28150             
28151             // update attributes
28152             if (this.tb.fields) {
28153                 this.tb.fields.each(function(e) {
28154                     if (e.stylename) {
28155                         e.setValue(sel.style[e.stylename]);
28156                         return;
28157                     } 
28158                    e.setValue(sel.getAttribute(e.attrname));
28159                 });
28160             }
28161             
28162             var hasStyles = false;
28163             for(var i in this.styles) {
28164                 hasStyles = true;
28165                 break;
28166             }
28167             
28168             // update styles
28169             if (hasStyles) { 
28170                 var st = this.tb.fields.item(0);
28171                 
28172                 st.store.removeAll();
28173                
28174                 
28175                 var cn = sel.className.split(/\s+/);
28176                 
28177                 var avs = [];
28178                 if (this.styles['*']) {
28179                     
28180                     Roo.each(this.styles['*'], function(v) {
28181                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28182                     });
28183                 }
28184                 if (this.styles[tn]) { 
28185                     Roo.each(this.styles[tn], function(v) {
28186                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28187                     });
28188                 }
28189                 
28190                 st.store.loadData(avs);
28191                 st.collapse();
28192                 st.setValue(cn);
28193             }
28194             // flag our selected Node.
28195             this.tb.selectedNode = sel;
28196            
28197            
28198             Roo.menu.MenuMgr.hideAll();
28199
28200         }
28201         
28202         if (!updateFooter) {
28203             //this.footDisp.dom.innerHTML = ''; 
28204             return;
28205         }
28206         // update the footer
28207         //
28208         var html = '';
28209         
28210         this.footerEls = ans.reverse();
28211         Roo.each(this.footerEls, function(a,i) {
28212             if (!a) { return; }
28213             html += html.length ? ' &gt; '  :  '';
28214             
28215             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28216             
28217         });
28218        
28219         // 
28220         var sz = this.footDisp.up('td').getSize();
28221         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28222         this.footDisp.dom.style.marginLeft = '5px';
28223         
28224         this.footDisp.dom.style.overflow = 'hidden';
28225         
28226         this.footDisp.dom.innerHTML = html;
28227             
28228         //this.editorsyncValue();
28229     },
28230      
28231     
28232    
28233        
28234     // private
28235     onDestroy : function(){
28236         if(this.rendered){
28237             
28238             this.tb.items.each(function(item){
28239                 if(item.menu){
28240                     item.menu.removeAll();
28241                     if(item.menu.el){
28242                         item.menu.el.destroy();
28243                     }
28244                 }
28245                 item.destroy();
28246             });
28247              
28248         }
28249     },
28250     onFirstFocus: function() {
28251         // need to do this for all the toolbars..
28252         this.tb.items.each(function(item){
28253            item.enable();
28254         });
28255     },
28256     buildToolbar: function(tlist, nm)
28257     {
28258         var editor = this.editor;
28259         var editorcore = this.editorcore;
28260          // create a new element.
28261         var wdiv = editor.wrap.createChild({
28262                 tag: 'div'
28263             }, editor.wrap.dom.firstChild.nextSibling, true);
28264         
28265        
28266         var tb = new Roo.Toolbar(wdiv);
28267         // add the name..
28268         
28269         tb.add(nm+ ":&nbsp;");
28270         
28271         var styles = [];
28272         for(var i in this.styles) {
28273             styles.push(i);
28274         }
28275         
28276         // styles...
28277         if (styles && styles.length) {
28278             
28279             // this needs a multi-select checkbox...
28280             tb.addField( new Roo.form.ComboBox({
28281                 store: new Roo.data.SimpleStore({
28282                     id : 'val',
28283                     fields: ['val', 'selected'],
28284                     data : [] 
28285                 }),
28286                 name : '-roo-edit-className',
28287                 attrname : 'className',
28288                 displayField: 'val',
28289                 typeAhead: false,
28290                 mode: 'local',
28291                 editable : false,
28292                 triggerAction: 'all',
28293                 emptyText:'Select Style',
28294                 selectOnFocus:true,
28295                 width: 130,
28296                 listeners : {
28297                     'select': function(c, r, i) {
28298                         // initial support only for on class per el..
28299                         tb.selectedNode.className =  r ? r.get('val') : '';
28300                         editorcore.syncValue();
28301                     }
28302                 }
28303     
28304             }));
28305         }
28306         
28307         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28308         var tbops = tbc.options;
28309         
28310         for (var i in tlist) {
28311             
28312             var item = tlist[i];
28313             tb.add(item.title + ":&nbsp;");
28314             
28315             
28316             //optname == used so you can configure the options available..
28317             var opts = item.opts ? item.opts : false;
28318             if (item.optname) {
28319                 opts = tbops[item.optname];
28320            
28321             }
28322             
28323             if (opts) {
28324                 // opts == pulldown..
28325                 tb.addField( new Roo.form.ComboBox({
28326                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28327                         id : 'val',
28328                         fields: ['val', 'display'],
28329                         data : opts  
28330                     }),
28331                     name : '-roo-edit-' + i,
28332                     attrname : i,
28333                     stylename : item.style ? item.style : false,
28334                     displayField: item.displayField ? item.displayField : 'val',
28335                     valueField :  'val',
28336                     typeAhead: false,
28337                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28338                     editable : false,
28339                     triggerAction: 'all',
28340                     emptyText:'Select',
28341                     selectOnFocus:true,
28342                     width: item.width ? item.width  : 130,
28343                     listeners : {
28344                         'select': function(c, r, i) {
28345                             if (c.stylename) {
28346                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28347                                 return;
28348                             }
28349                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28350                         }
28351                     }
28352
28353                 }));
28354                 continue;
28355                     
28356                  
28357                 
28358                 tb.addField( new Roo.form.TextField({
28359                     name: i,
28360                     width: 100,
28361                     //allowBlank:false,
28362                     value: ''
28363                 }));
28364                 continue;
28365             }
28366             tb.addField( new Roo.form.TextField({
28367                 name: '-roo-edit-' + i,
28368                 attrname : i,
28369                 
28370                 width: item.width,
28371                 //allowBlank:true,
28372                 value: '',
28373                 listeners: {
28374                     'change' : function(f, nv, ov) {
28375                         tb.selectedNode.setAttribute(f.attrname, nv);
28376                     }
28377                 }
28378             }));
28379              
28380         }
28381         
28382         var _this = this;
28383         
28384         if(nm == 'BODY'){
28385             tb.addSeparator();
28386         
28387             tb.addButton( {
28388                 text: 'Stylesheets',
28389
28390                 listeners : {
28391                     click : function ()
28392                     {
28393                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28394                     }
28395                 }
28396             });
28397         }
28398         
28399         tb.addFill();
28400         tb.addButton( {
28401             text: 'Remove Tag',
28402     
28403             listeners : {
28404                 click : function ()
28405                 {
28406                     // remove
28407                     // undo does not work.
28408                      
28409                     var sn = tb.selectedNode;
28410                     
28411                     var pn = sn.parentNode;
28412                     
28413                     var stn =  sn.childNodes[0];
28414                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28415                     while (sn.childNodes.length) {
28416                         var node = sn.childNodes[0];
28417                         sn.removeChild(node);
28418                         //Roo.log(node);
28419                         pn.insertBefore(node, sn);
28420                         
28421                     }
28422                     pn.removeChild(sn);
28423                     var range = editorcore.createRange();
28424         
28425                     range.setStart(stn,0);
28426                     range.setEnd(en,0); //????
28427                     //range.selectNode(sel);
28428                     
28429                     
28430                     var selection = editorcore.getSelection();
28431                     selection.removeAllRanges();
28432                     selection.addRange(range);
28433                     
28434                     
28435                     
28436                     //_this.updateToolbar(null, null, pn);
28437                     _this.updateToolbar(null, null, null);
28438                     _this.footDisp.dom.innerHTML = ''; 
28439                 }
28440             }
28441             
28442                     
28443                 
28444             
28445         });
28446         
28447         
28448         tb.el.on('click', function(e){
28449             e.preventDefault(); // what does this do?
28450         });
28451         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28452         tb.el.hide();
28453         tb.name = nm;
28454         // dont need to disable them... as they will get hidden
28455         return tb;
28456          
28457         
28458     },
28459     buildFooter : function()
28460     {
28461         
28462         var fel = this.editor.wrap.createChild();
28463         this.footer = new Roo.Toolbar(fel);
28464         // toolbar has scrolly on left / right?
28465         var footDisp= new Roo.Toolbar.Fill();
28466         var _t = this;
28467         this.footer.add(
28468             {
28469                 text : '&lt;',
28470                 xtype: 'Button',
28471                 handler : function() {
28472                     _t.footDisp.scrollTo('left',0,true)
28473                 }
28474             }
28475         );
28476         this.footer.add( footDisp );
28477         this.footer.add( 
28478             {
28479                 text : '&gt;',
28480                 xtype: 'Button',
28481                 handler : function() {
28482                     // no animation..
28483                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28484                 }
28485             }
28486         );
28487         var fel = Roo.get(footDisp.el);
28488         fel.addClass('x-editor-context');
28489         this.footDispWrap = fel; 
28490         this.footDispWrap.overflow  = 'hidden';
28491         
28492         this.footDisp = fel.createChild();
28493         this.footDispWrap.on('click', this.onContextClick, this)
28494         
28495         
28496     },
28497     onContextClick : function (ev,dom)
28498     {
28499         ev.preventDefault();
28500         var  cn = dom.className;
28501         //Roo.log(cn);
28502         if (!cn.match(/x-ed-loc-/)) {
28503             return;
28504         }
28505         var n = cn.split('-').pop();
28506         var ans = this.footerEls;
28507         var sel = ans[n];
28508         
28509          // pick
28510         var range = this.editorcore.createRange();
28511         
28512         range.selectNodeContents(sel);
28513         //range.selectNode(sel);
28514         
28515         
28516         var selection = this.editorcore.getSelection();
28517         selection.removeAllRanges();
28518         selection.addRange(range);
28519         
28520         
28521         
28522         this.updateToolbar(null, null, sel);
28523         
28524         
28525     }
28526     
28527     
28528     
28529     
28530     
28531 });
28532
28533
28534
28535
28536
28537 /*
28538  * Based on:
28539  * Ext JS Library 1.1.1
28540  * Copyright(c) 2006-2007, Ext JS, LLC.
28541  *
28542  * Originally Released Under LGPL - original licence link has changed is not relivant.
28543  *
28544  * Fork - LGPL
28545  * <script type="text/javascript">
28546  */
28547  
28548 /**
28549  * @class Roo.form.BasicForm
28550  * @extends Roo.util.Observable
28551  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28552  * @constructor
28553  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28554  * @param {Object} config Configuration options
28555  */
28556 Roo.form.BasicForm = function(el, config){
28557     this.allItems = [];
28558     this.childForms = [];
28559     Roo.apply(this, config);
28560     /*
28561      * The Roo.form.Field items in this form.
28562      * @type MixedCollection
28563      */
28564      
28565      
28566     this.items = new Roo.util.MixedCollection(false, function(o){
28567         return o.id || (o.id = Roo.id());
28568     });
28569     this.addEvents({
28570         /**
28571          * @event beforeaction
28572          * Fires before any action is performed. Return false to cancel the action.
28573          * @param {Form} this
28574          * @param {Action} action The action to be performed
28575          */
28576         beforeaction: true,
28577         /**
28578          * @event actionfailed
28579          * Fires when an action fails.
28580          * @param {Form} this
28581          * @param {Action} action The action that failed
28582          */
28583         actionfailed : true,
28584         /**
28585          * @event actioncomplete
28586          * Fires when an action is completed.
28587          * @param {Form} this
28588          * @param {Action} action The action that completed
28589          */
28590         actioncomplete : true
28591     });
28592     if(el){
28593         this.initEl(el);
28594     }
28595     Roo.form.BasicForm.superclass.constructor.call(this);
28596 };
28597
28598 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28599     /**
28600      * @cfg {String} method
28601      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28602      */
28603     /**
28604      * @cfg {DataReader} reader
28605      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28606      * This is optional as there is built-in support for processing JSON.
28607      */
28608     /**
28609      * @cfg {DataReader} errorReader
28610      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28611      * This is completely optional as there is built-in support for processing JSON.
28612      */
28613     /**
28614      * @cfg {String} url
28615      * The URL to use for form actions if one isn't supplied in the action options.
28616      */
28617     /**
28618      * @cfg {Boolean} fileUpload
28619      * Set to true if this form is a file upload.
28620      */
28621      
28622     /**
28623      * @cfg {Object} baseParams
28624      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28625      */
28626      /**
28627      
28628     /**
28629      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28630      */
28631     timeout: 30,
28632
28633     // private
28634     activeAction : null,
28635
28636     /**
28637      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28638      * or setValues() data instead of when the form was first created.
28639      */
28640     trackResetOnLoad : false,
28641     
28642     
28643     /**
28644      * childForms - used for multi-tab forms
28645      * @type {Array}
28646      */
28647     childForms : false,
28648     
28649     /**
28650      * allItems - full list of fields.
28651      * @type {Array}
28652      */
28653     allItems : false,
28654     
28655     /**
28656      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28657      * element by passing it or its id or mask the form itself by passing in true.
28658      * @type Mixed
28659      */
28660     waitMsgTarget : false,
28661
28662     // private
28663     initEl : function(el){
28664         this.el = Roo.get(el);
28665         this.id = this.el.id || Roo.id();
28666         this.el.on('submit', this.onSubmit, this);
28667         this.el.addClass('x-form');
28668     },
28669
28670     // private
28671     onSubmit : function(e){
28672         e.stopEvent();
28673     },
28674
28675     /**
28676      * Returns true if client-side validation on the form is successful.
28677      * @return Boolean
28678      */
28679     isValid : function(){
28680         var valid = true;
28681         this.items.each(function(f){
28682            if(!f.validate()){
28683                valid = false;
28684            }
28685         });
28686         return valid;
28687     },
28688
28689     /**
28690      * Returns true if any fields in this form have changed since their original load.
28691      * @return Boolean
28692      */
28693     isDirty : function(){
28694         var dirty = false;
28695         this.items.each(function(f){
28696            if(f.isDirty()){
28697                dirty = true;
28698                return false;
28699            }
28700         });
28701         return dirty;
28702     },
28703
28704     /**
28705      * Performs a predefined action (submit or load) or custom actions you define on this form.
28706      * @param {String} actionName The name of the action type
28707      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28708      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28709      * accept other config options):
28710      * <pre>
28711 Property          Type             Description
28712 ----------------  ---------------  ----------------------------------------------------------------------------------
28713 url               String           The url for the action (defaults to the form's url)
28714 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28715 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28716 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28717                                    validate the form on the client (defaults to false)
28718      * </pre>
28719      * @return {BasicForm} this
28720      */
28721     doAction : function(action, options){
28722         if(typeof action == 'string'){
28723             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28724         }
28725         if(this.fireEvent('beforeaction', this, action) !== false){
28726             this.beforeAction(action);
28727             action.run.defer(100, action);
28728         }
28729         return this;
28730     },
28731
28732     /**
28733      * Shortcut to do a submit action.
28734      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28735      * @return {BasicForm} this
28736      */
28737     submit : function(options){
28738         this.doAction('submit', options);
28739         return this;
28740     },
28741
28742     /**
28743      * Shortcut to do a load action.
28744      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28745      * @return {BasicForm} this
28746      */
28747     load : function(options){
28748         this.doAction('load', options);
28749         return this;
28750     },
28751
28752     /**
28753      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28754      * @param {Record} record The record to edit
28755      * @return {BasicForm} this
28756      */
28757     updateRecord : function(record){
28758         record.beginEdit();
28759         var fs = record.fields;
28760         fs.each(function(f){
28761             var field = this.findField(f.name);
28762             if(field){
28763                 record.set(f.name, field.getValue());
28764             }
28765         }, this);
28766         record.endEdit();
28767         return this;
28768     },
28769
28770     /**
28771      * Loads an Roo.data.Record into this form.
28772      * @param {Record} record The record to load
28773      * @return {BasicForm} this
28774      */
28775     loadRecord : function(record){
28776         this.setValues(record.data);
28777         return this;
28778     },
28779
28780     // private
28781     beforeAction : function(action){
28782         var o = action.options;
28783         
28784        
28785         if(this.waitMsgTarget === true){
28786             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28787         }else if(this.waitMsgTarget){
28788             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28789             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28790         }else {
28791             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28792         }
28793          
28794     },
28795
28796     // private
28797     afterAction : function(action, success){
28798         this.activeAction = null;
28799         var o = action.options;
28800         
28801         if(this.waitMsgTarget === true){
28802             this.el.unmask();
28803         }else if(this.waitMsgTarget){
28804             this.waitMsgTarget.unmask();
28805         }else{
28806             Roo.MessageBox.updateProgress(1);
28807             Roo.MessageBox.hide();
28808         }
28809          
28810         if(success){
28811             if(o.reset){
28812                 this.reset();
28813             }
28814             Roo.callback(o.success, o.scope, [this, action]);
28815             this.fireEvent('actioncomplete', this, action);
28816             
28817         }else{
28818             
28819             // failure condition..
28820             // we have a scenario where updates need confirming.
28821             // eg. if a locking scenario exists..
28822             // we look for { errors : { needs_confirm : true }} in the response.
28823             if (
28824                 (typeof(action.result) != 'undefined')  &&
28825                 (typeof(action.result.errors) != 'undefined')  &&
28826                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28827            ){
28828                 var _t = this;
28829                 Roo.MessageBox.confirm(
28830                     "Change requires confirmation",
28831                     action.result.errorMsg,
28832                     function(r) {
28833                         if (r != 'yes') {
28834                             return;
28835                         }
28836                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28837                     }
28838                     
28839                 );
28840                 
28841                 
28842                 
28843                 return;
28844             }
28845             
28846             Roo.callback(o.failure, o.scope, [this, action]);
28847             // show an error message if no failed handler is set..
28848             if (!this.hasListener('actionfailed')) {
28849                 Roo.MessageBox.alert("Error",
28850                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28851                         action.result.errorMsg :
28852                         "Saving Failed, please check your entries or try again"
28853                 );
28854             }
28855             
28856             this.fireEvent('actionfailed', this, action);
28857         }
28858         
28859     },
28860
28861     /**
28862      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28863      * @param {String} id The value to search for
28864      * @return Field
28865      */
28866     findField : function(id){
28867         var field = this.items.get(id);
28868         if(!field){
28869             this.items.each(function(f){
28870                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28871                     field = f;
28872                     return false;
28873                 }
28874             });
28875         }
28876         return field || null;
28877     },
28878
28879     /**
28880      * Add a secondary form to this one, 
28881      * Used to provide tabbed forms. One form is primary, with hidden values 
28882      * which mirror the elements from the other forms.
28883      * 
28884      * @param {Roo.form.Form} form to add.
28885      * 
28886      */
28887     addForm : function(form)
28888     {
28889        
28890         if (this.childForms.indexOf(form) > -1) {
28891             // already added..
28892             return;
28893         }
28894         this.childForms.push(form);
28895         var n = '';
28896         Roo.each(form.allItems, function (fe) {
28897             
28898             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28899             if (this.findField(n)) { // already added..
28900                 return;
28901             }
28902             var add = new Roo.form.Hidden({
28903                 name : n
28904             });
28905             add.render(this.el);
28906             
28907             this.add( add );
28908         }, this);
28909         
28910     },
28911     /**
28912      * Mark fields in this form invalid in bulk.
28913      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28914      * @return {BasicForm} this
28915      */
28916     markInvalid : function(errors){
28917         if(errors instanceof Array){
28918             for(var i = 0, len = errors.length; i < len; i++){
28919                 var fieldError = errors[i];
28920                 var f = this.findField(fieldError.id);
28921                 if(f){
28922                     f.markInvalid(fieldError.msg);
28923                 }
28924             }
28925         }else{
28926             var field, id;
28927             for(id in errors){
28928                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28929                     field.markInvalid(errors[id]);
28930                 }
28931             }
28932         }
28933         Roo.each(this.childForms || [], function (f) {
28934             f.markInvalid(errors);
28935         });
28936         
28937         return this;
28938     },
28939
28940     /**
28941      * Set values for fields in this form in bulk.
28942      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28943      * @return {BasicForm} this
28944      */
28945     setValues : function(values){
28946         if(values instanceof Array){ // array of objects
28947             for(var i = 0, len = values.length; i < len; i++){
28948                 var v = values[i];
28949                 var f = this.findField(v.id);
28950                 if(f){
28951                     f.setValue(v.value);
28952                     if(this.trackResetOnLoad){
28953                         f.originalValue = f.getValue();
28954                     }
28955                 }
28956             }
28957         }else{ // object hash
28958             var field, id;
28959             for(id in values){
28960                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28961                     
28962                     if (field.setFromData && 
28963                         field.valueField && 
28964                         field.displayField &&
28965                         // combos' with local stores can 
28966                         // be queried via setValue()
28967                         // to set their value..
28968                         (field.store && !field.store.isLocal)
28969                         ) {
28970                         // it's a combo
28971                         var sd = { };
28972                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28973                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28974                         field.setFromData(sd);
28975                         
28976                     } else {
28977                         field.setValue(values[id]);
28978                     }
28979                     
28980                     
28981                     if(this.trackResetOnLoad){
28982                         field.originalValue = field.getValue();
28983                     }
28984                 }
28985             }
28986         }
28987          
28988         Roo.each(this.childForms || [], function (f) {
28989             f.setValues(values);
28990         });
28991                 
28992         return this;
28993     },
28994
28995     /**
28996      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28997      * they are returned as an array.
28998      * @param {Boolean} asString
28999      * @return {Object}
29000      */
29001     getValues : function(asString){
29002         if (this.childForms) {
29003             // copy values from the child forms
29004             Roo.each(this.childForms, function (f) {
29005                 this.setValues(f.getValues());
29006             }, this);
29007         }
29008         
29009         
29010         
29011         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29012         if(asString === true){
29013             return fs;
29014         }
29015         return Roo.urlDecode(fs);
29016     },
29017     
29018     /**
29019      * Returns the fields in this form as an object with key/value pairs. 
29020      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29021      * @return {Object}
29022      */
29023     getFieldValues : function(with_hidden)
29024     {
29025         if (this.childForms) {
29026             // copy values from the child forms
29027             // should this call getFieldValues - probably not as we do not currently copy
29028             // hidden fields when we generate..
29029             Roo.each(this.childForms, function (f) {
29030                 this.setValues(f.getValues());
29031             }, this);
29032         }
29033         
29034         var ret = {};
29035         this.items.each(function(f){
29036             if (!f.getName()) {
29037                 return;
29038             }
29039             var v = f.getValue();
29040             if (f.inputType =='radio') {
29041                 if (typeof(ret[f.getName()]) == 'undefined') {
29042                     ret[f.getName()] = ''; // empty..
29043                 }
29044                 
29045                 if (!f.el.dom.checked) {
29046                     return;
29047                     
29048                 }
29049                 v = f.el.dom.value;
29050                 
29051             }
29052             
29053             // not sure if this supported any more..
29054             if ((typeof(v) == 'object') && f.getRawValue) {
29055                 v = f.getRawValue() ; // dates..
29056             }
29057             // combo boxes where name != hiddenName...
29058             if (f.name != f.getName()) {
29059                 ret[f.name] = f.getRawValue();
29060             }
29061             ret[f.getName()] = v;
29062         });
29063         
29064         return ret;
29065     },
29066
29067     /**
29068      * Clears all invalid messages in this form.
29069      * @return {BasicForm} this
29070      */
29071     clearInvalid : function(){
29072         this.items.each(function(f){
29073            f.clearInvalid();
29074         });
29075         
29076         Roo.each(this.childForms || [], function (f) {
29077             f.clearInvalid();
29078         });
29079         
29080         
29081         return this;
29082     },
29083
29084     /**
29085      * Resets this form.
29086      * @return {BasicForm} this
29087      */
29088     reset : function(){
29089         this.items.each(function(f){
29090             f.reset();
29091         });
29092         
29093         Roo.each(this.childForms || [], function (f) {
29094             f.reset();
29095         });
29096        
29097         
29098         return this;
29099     },
29100
29101     /**
29102      * Add Roo.form components to this form.
29103      * @param {Field} field1
29104      * @param {Field} field2 (optional)
29105      * @param {Field} etc (optional)
29106      * @return {BasicForm} this
29107      */
29108     add : function(){
29109         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29110         return this;
29111     },
29112
29113
29114     /**
29115      * Removes a field from the items collection (does NOT remove its markup).
29116      * @param {Field} field
29117      * @return {BasicForm} this
29118      */
29119     remove : function(field){
29120         this.items.remove(field);
29121         return this;
29122     },
29123
29124     /**
29125      * Looks at the fields in this form, checks them for an id attribute,
29126      * and calls applyTo on the existing dom element with that id.
29127      * @return {BasicForm} this
29128      */
29129     render : function(){
29130         this.items.each(function(f){
29131             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29132                 f.applyTo(f.id);
29133             }
29134         });
29135         return this;
29136     },
29137
29138     /**
29139      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29140      * @param {Object} values
29141      * @return {BasicForm} this
29142      */
29143     applyToFields : function(o){
29144         this.items.each(function(f){
29145            Roo.apply(f, o);
29146         });
29147         return this;
29148     },
29149
29150     /**
29151      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29152      * @param {Object} values
29153      * @return {BasicForm} this
29154      */
29155     applyIfToFields : function(o){
29156         this.items.each(function(f){
29157            Roo.applyIf(f, o);
29158         });
29159         return this;
29160     }
29161 });
29162
29163 // back compat
29164 Roo.BasicForm = Roo.form.BasicForm;/*
29165  * Based on:
29166  * Ext JS Library 1.1.1
29167  * Copyright(c) 2006-2007, Ext JS, LLC.
29168  *
29169  * Originally Released Under LGPL - original licence link has changed is not relivant.
29170  *
29171  * Fork - LGPL
29172  * <script type="text/javascript">
29173  */
29174
29175 /**
29176  * @class Roo.form.Form
29177  * @extends Roo.form.BasicForm
29178  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29179  * @constructor
29180  * @param {Object} config Configuration options
29181  */
29182 Roo.form.Form = function(config){
29183     var xitems =  [];
29184     if (config.items) {
29185         xitems = config.items;
29186         delete config.items;
29187     }
29188    
29189     
29190     Roo.form.Form.superclass.constructor.call(this, null, config);
29191     this.url = this.url || this.action;
29192     if(!this.root){
29193         this.root = new Roo.form.Layout(Roo.applyIf({
29194             id: Roo.id()
29195         }, config));
29196     }
29197     this.active = this.root;
29198     /**
29199      * Array of all the buttons that have been added to this form via {@link addButton}
29200      * @type Array
29201      */
29202     this.buttons = [];
29203     this.allItems = [];
29204     this.addEvents({
29205         /**
29206          * @event clientvalidation
29207          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29208          * @param {Form} this
29209          * @param {Boolean} valid true if the form has passed client-side validation
29210          */
29211         clientvalidation: true,
29212         /**
29213          * @event rendered
29214          * Fires when the form is rendered
29215          * @param {Roo.form.Form} form
29216          */
29217         rendered : true
29218     });
29219     
29220     if (this.progressUrl) {
29221             // push a hidden field onto the list of fields..
29222             this.addxtype( {
29223                     xns: Roo.form, 
29224                     xtype : 'Hidden', 
29225                     name : 'UPLOAD_IDENTIFIER' 
29226             });
29227         }
29228         
29229     
29230     Roo.each(xitems, this.addxtype, this);
29231     
29232     
29233     
29234 };
29235
29236 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29237     /**
29238      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29239      */
29240     /**
29241      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29242      */
29243     /**
29244      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29245      */
29246     buttonAlign:'center',
29247
29248     /**
29249      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29250      */
29251     minButtonWidth:75,
29252
29253     /**
29254      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29255      * This property cascades to child containers if not set.
29256      */
29257     labelAlign:'left',
29258
29259     /**
29260      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29261      * fires a looping event with that state. This is required to bind buttons to the valid
29262      * state using the config value formBind:true on the button.
29263      */
29264     monitorValid : false,
29265
29266     /**
29267      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29268      */
29269     monitorPoll : 200,
29270     
29271     /**
29272      * @cfg {String} progressUrl - Url to return progress data 
29273      */
29274     
29275     progressUrl : false,
29276   
29277     /**
29278      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29279      * fields are added and the column is closed. If no fields are passed the column remains open
29280      * until end() is called.
29281      * @param {Object} config The config to pass to the column
29282      * @param {Field} field1 (optional)
29283      * @param {Field} field2 (optional)
29284      * @param {Field} etc (optional)
29285      * @return Column The column container object
29286      */
29287     column : function(c){
29288         var col = new Roo.form.Column(c);
29289         this.start(col);
29290         if(arguments.length > 1){ // duplicate code required because of Opera
29291             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29292             this.end();
29293         }
29294         return col;
29295     },
29296
29297     /**
29298      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29299      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29300      * until end() is called.
29301      * @param {Object} config The config to pass to the fieldset
29302      * @param {Field} field1 (optional)
29303      * @param {Field} field2 (optional)
29304      * @param {Field} etc (optional)
29305      * @return FieldSet The fieldset container object
29306      */
29307     fieldset : function(c){
29308         var fs = new Roo.form.FieldSet(c);
29309         this.start(fs);
29310         if(arguments.length > 1){ // duplicate code required because of Opera
29311             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29312             this.end();
29313         }
29314         return fs;
29315     },
29316
29317     /**
29318      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29319      * fields are added and the container is closed. If no fields are passed the container remains open
29320      * until end() is called.
29321      * @param {Object} config The config to pass to the Layout
29322      * @param {Field} field1 (optional)
29323      * @param {Field} field2 (optional)
29324      * @param {Field} etc (optional)
29325      * @return Layout The container object
29326      */
29327     container : function(c){
29328         var l = new Roo.form.Layout(c);
29329         this.start(l);
29330         if(arguments.length > 1){ // duplicate code required because of Opera
29331             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29332             this.end();
29333         }
29334         return l;
29335     },
29336
29337     /**
29338      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29339      * @param {Object} container A Roo.form.Layout or subclass of Layout
29340      * @return {Form} this
29341      */
29342     start : function(c){
29343         // cascade label info
29344         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29345         this.active.stack.push(c);
29346         c.ownerCt = this.active;
29347         this.active = c;
29348         return this;
29349     },
29350
29351     /**
29352      * Closes the current open container
29353      * @return {Form} this
29354      */
29355     end : function(){
29356         if(this.active == this.root){
29357             return this;
29358         }
29359         this.active = this.active.ownerCt;
29360         return this;
29361     },
29362
29363     /**
29364      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29365      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29366      * as the label of the field.
29367      * @param {Field} field1
29368      * @param {Field} field2 (optional)
29369      * @param {Field} etc. (optional)
29370      * @return {Form} this
29371      */
29372     add : function(){
29373         this.active.stack.push.apply(this.active.stack, arguments);
29374         this.allItems.push.apply(this.allItems,arguments);
29375         var r = [];
29376         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29377             if(a[i].isFormField){
29378                 r.push(a[i]);
29379             }
29380         }
29381         if(r.length > 0){
29382             Roo.form.Form.superclass.add.apply(this, r);
29383         }
29384         return this;
29385     },
29386     
29387
29388     
29389     
29390     
29391      /**
29392      * Find any element that has been added to a form, using it's ID or name
29393      * This can include framesets, columns etc. along with regular fields..
29394      * @param {String} id - id or name to find.
29395      
29396      * @return {Element} e - or false if nothing found.
29397      */
29398     findbyId : function(id)
29399     {
29400         var ret = false;
29401         if (!id) {
29402             return ret;
29403         }
29404         Roo.each(this.allItems, function(f){
29405             if (f.id == id || f.name == id ){
29406                 ret = f;
29407                 return false;
29408             }
29409         });
29410         return ret;
29411     },
29412
29413     
29414     
29415     /**
29416      * Render this form into the passed container. This should only be called once!
29417      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29418      * @return {Form} this
29419      */
29420     render : function(ct)
29421     {
29422         
29423         
29424         
29425         ct = Roo.get(ct);
29426         var o = this.autoCreate || {
29427             tag: 'form',
29428             method : this.method || 'POST',
29429             id : this.id || Roo.id()
29430         };
29431         this.initEl(ct.createChild(o));
29432
29433         this.root.render(this.el);
29434         
29435        
29436              
29437         this.items.each(function(f){
29438             f.render('x-form-el-'+f.id);
29439         });
29440
29441         if(this.buttons.length > 0){
29442             // tables are required to maintain order and for correct IE layout
29443             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29444                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29445                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29446             }}, null, true);
29447             var tr = tb.getElementsByTagName('tr')[0];
29448             for(var i = 0, len = this.buttons.length; i < len; i++) {
29449                 var b = this.buttons[i];
29450                 var td = document.createElement('td');
29451                 td.className = 'x-form-btn-td';
29452                 b.render(tr.appendChild(td));
29453             }
29454         }
29455         if(this.monitorValid){ // initialize after render
29456             this.startMonitoring();
29457         }
29458         this.fireEvent('rendered', this);
29459         return this;
29460     },
29461
29462     /**
29463      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29464      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29465      * object or a valid Roo.DomHelper element config
29466      * @param {Function} handler The function called when the button is clicked
29467      * @param {Object} scope (optional) The scope of the handler function
29468      * @return {Roo.Button}
29469      */
29470     addButton : function(config, handler, scope){
29471         var bc = {
29472             handler: handler,
29473             scope: scope,
29474             minWidth: this.minButtonWidth,
29475             hideParent:true
29476         };
29477         if(typeof config == "string"){
29478             bc.text = config;
29479         }else{
29480             Roo.apply(bc, config);
29481         }
29482         var btn = new Roo.Button(null, bc);
29483         this.buttons.push(btn);
29484         return btn;
29485     },
29486
29487      /**
29488      * Adds a series of form elements (using the xtype property as the factory method.
29489      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29490      * @param {Object} config 
29491      */
29492     
29493     addxtype : function()
29494     {
29495         var ar = Array.prototype.slice.call(arguments, 0);
29496         var ret = false;
29497         for(var i = 0; i < ar.length; i++) {
29498             if (!ar[i]) {
29499                 continue; // skip -- if this happends something invalid got sent, we 
29500                 // should ignore it, as basically that interface element will not show up
29501                 // and that should be pretty obvious!!
29502             }
29503             
29504             if (Roo.form[ar[i].xtype]) {
29505                 ar[i].form = this;
29506                 var fe = Roo.factory(ar[i], Roo.form);
29507                 if (!ret) {
29508                     ret = fe;
29509                 }
29510                 fe.form = this;
29511                 if (fe.store) {
29512                     fe.store.form = this;
29513                 }
29514                 if (fe.isLayout) {  
29515                          
29516                     this.start(fe);
29517                     this.allItems.push(fe);
29518                     if (fe.items && fe.addxtype) {
29519                         fe.addxtype.apply(fe, fe.items);
29520                         delete fe.items;
29521                     }
29522                      this.end();
29523                     continue;
29524                 }
29525                 
29526                 
29527                  
29528                 this.add(fe);
29529               //  console.log('adding ' + ar[i].xtype);
29530             }
29531             if (ar[i].xtype == 'Button') {  
29532                 //console.log('adding button');
29533                 //console.log(ar[i]);
29534                 this.addButton(ar[i]);
29535                 this.allItems.push(fe);
29536                 continue;
29537             }
29538             
29539             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29540                 alert('end is not supported on xtype any more, use items');
29541             //    this.end();
29542             //    //console.log('adding end');
29543             }
29544             
29545         }
29546         return ret;
29547     },
29548     
29549     /**
29550      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29551      * option "monitorValid"
29552      */
29553     startMonitoring : function(){
29554         if(!this.bound){
29555             this.bound = true;
29556             Roo.TaskMgr.start({
29557                 run : this.bindHandler,
29558                 interval : this.monitorPoll || 200,
29559                 scope: this
29560             });
29561         }
29562     },
29563
29564     /**
29565      * Stops monitoring of the valid state of this form
29566      */
29567     stopMonitoring : function(){
29568         this.bound = false;
29569     },
29570
29571     // private
29572     bindHandler : function(){
29573         if(!this.bound){
29574             return false; // stops binding
29575         }
29576         var valid = true;
29577         this.items.each(function(f){
29578             if(!f.isValid(true)){
29579                 valid = false;
29580                 return false;
29581             }
29582         });
29583         for(var i = 0, len = this.buttons.length; i < len; i++){
29584             var btn = this.buttons[i];
29585             if(btn.formBind === true && btn.disabled === valid){
29586                 btn.setDisabled(!valid);
29587             }
29588         }
29589         this.fireEvent('clientvalidation', this, valid);
29590     }
29591     
29592     
29593     
29594     
29595     
29596     
29597     
29598     
29599 });
29600
29601
29602 // back compat
29603 Roo.Form = Roo.form.Form;
29604 /*
29605  * Based on:
29606  * Ext JS Library 1.1.1
29607  * Copyright(c) 2006-2007, Ext JS, LLC.
29608  *
29609  * Originally Released Under LGPL - original licence link has changed is not relivant.
29610  *
29611  * Fork - LGPL
29612  * <script type="text/javascript">
29613  */
29614
29615 // as we use this in bootstrap.
29616 Roo.namespace('Roo.form');
29617  /**
29618  * @class Roo.form.Action
29619  * Internal Class used to handle form actions
29620  * @constructor
29621  * @param {Roo.form.BasicForm} el The form element or its id
29622  * @param {Object} config Configuration options
29623  */
29624
29625  
29626  
29627 // define the action interface
29628 Roo.form.Action = function(form, options){
29629     this.form = form;
29630     this.options = options || {};
29631 };
29632 /**
29633  * Client Validation Failed
29634  * @const 
29635  */
29636 Roo.form.Action.CLIENT_INVALID = 'client';
29637 /**
29638  * Server Validation Failed
29639  * @const 
29640  */
29641 Roo.form.Action.SERVER_INVALID = 'server';
29642  /**
29643  * Connect to Server Failed
29644  * @const 
29645  */
29646 Roo.form.Action.CONNECT_FAILURE = 'connect';
29647 /**
29648  * Reading Data from Server Failed
29649  * @const 
29650  */
29651 Roo.form.Action.LOAD_FAILURE = 'load';
29652
29653 Roo.form.Action.prototype = {
29654     type : 'default',
29655     failureType : undefined,
29656     response : undefined,
29657     result : undefined,
29658
29659     // interface method
29660     run : function(options){
29661
29662     },
29663
29664     // interface method
29665     success : function(response){
29666
29667     },
29668
29669     // interface method
29670     handleResponse : function(response){
29671
29672     },
29673
29674     // default connection failure
29675     failure : function(response){
29676         
29677         this.response = response;
29678         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29679         this.form.afterAction(this, false);
29680     },
29681
29682     processResponse : function(response){
29683         this.response = response;
29684         if(!response.responseText){
29685             return true;
29686         }
29687         this.result = this.handleResponse(response);
29688         return this.result;
29689     },
29690
29691     // utility functions used internally
29692     getUrl : function(appendParams){
29693         var url = this.options.url || this.form.url || this.form.el.dom.action;
29694         if(appendParams){
29695             var p = this.getParams();
29696             if(p){
29697                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29698             }
29699         }
29700         return url;
29701     },
29702
29703     getMethod : function(){
29704         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29705     },
29706
29707     getParams : function(){
29708         var bp = this.form.baseParams;
29709         var p = this.options.params;
29710         if(p){
29711             if(typeof p == "object"){
29712                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29713             }else if(typeof p == 'string' && bp){
29714                 p += '&' + Roo.urlEncode(bp);
29715             }
29716         }else if(bp){
29717             p = Roo.urlEncode(bp);
29718         }
29719         return p;
29720     },
29721
29722     createCallback : function(){
29723         return {
29724             success: this.success,
29725             failure: this.failure,
29726             scope: this,
29727             timeout: (this.form.timeout*1000),
29728             upload: this.form.fileUpload ? this.success : undefined
29729         };
29730     }
29731 };
29732
29733 Roo.form.Action.Submit = function(form, options){
29734     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29735 };
29736
29737 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29738     type : 'submit',
29739
29740     haveProgress : false,
29741     uploadComplete : false,
29742     
29743     // uploadProgress indicator.
29744     uploadProgress : function()
29745     {
29746         if (!this.form.progressUrl) {
29747             return;
29748         }
29749         
29750         if (!this.haveProgress) {
29751             Roo.MessageBox.progress("Uploading", "Uploading");
29752         }
29753         if (this.uploadComplete) {
29754            Roo.MessageBox.hide();
29755            return;
29756         }
29757         
29758         this.haveProgress = true;
29759    
29760         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29761         
29762         var c = new Roo.data.Connection();
29763         c.request({
29764             url : this.form.progressUrl,
29765             params: {
29766                 id : uid
29767             },
29768             method: 'GET',
29769             success : function(req){
29770                //console.log(data);
29771                 var rdata = false;
29772                 var edata;
29773                 try  {
29774                    rdata = Roo.decode(req.responseText)
29775                 } catch (e) {
29776                     Roo.log("Invalid data from server..");
29777                     Roo.log(edata);
29778                     return;
29779                 }
29780                 if (!rdata || !rdata.success) {
29781                     Roo.log(rdata);
29782                     Roo.MessageBox.alert(Roo.encode(rdata));
29783                     return;
29784                 }
29785                 var data = rdata.data;
29786                 
29787                 if (this.uploadComplete) {
29788                    Roo.MessageBox.hide();
29789                    return;
29790                 }
29791                    
29792                 if (data){
29793                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29794                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29795                     );
29796                 }
29797                 this.uploadProgress.defer(2000,this);
29798             },
29799        
29800             failure: function(data) {
29801                 Roo.log('progress url failed ');
29802                 Roo.log(data);
29803             },
29804             scope : this
29805         });
29806            
29807     },
29808     
29809     
29810     run : function()
29811     {
29812         // run get Values on the form, so it syncs any secondary forms.
29813         this.form.getValues();
29814         
29815         var o = this.options;
29816         var method = this.getMethod();
29817         var isPost = method == 'POST';
29818         if(o.clientValidation === false || this.form.isValid()){
29819             
29820             if (this.form.progressUrl) {
29821                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29822                     (new Date() * 1) + '' + Math.random());
29823                     
29824             } 
29825             
29826             
29827             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29828                 form:this.form.el.dom,
29829                 url:this.getUrl(!isPost),
29830                 method: method,
29831                 params:isPost ? this.getParams() : null,
29832                 isUpload: this.form.fileUpload
29833             }));
29834             
29835             this.uploadProgress();
29836
29837         }else if (o.clientValidation !== false){ // client validation failed
29838             this.failureType = Roo.form.Action.CLIENT_INVALID;
29839             this.form.afterAction(this, false);
29840         }
29841     },
29842
29843     success : function(response)
29844     {
29845         this.uploadComplete= true;
29846         if (this.haveProgress) {
29847             Roo.MessageBox.hide();
29848         }
29849         
29850         
29851         var result = this.processResponse(response);
29852         if(result === true || result.success){
29853             this.form.afterAction(this, true);
29854             return;
29855         }
29856         if(result.errors){
29857             this.form.markInvalid(result.errors);
29858             this.failureType = Roo.form.Action.SERVER_INVALID;
29859         }
29860         this.form.afterAction(this, false);
29861     },
29862     failure : function(response)
29863     {
29864         this.uploadComplete= true;
29865         if (this.haveProgress) {
29866             Roo.MessageBox.hide();
29867         }
29868         
29869         this.response = response;
29870         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29871         this.form.afterAction(this, false);
29872     },
29873     
29874     handleResponse : function(response){
29875         if(this.form.errorReader){
29876             var rs = this.form.errorReader.read(response);
29877             var errors = [];
29878             if(rs.records){
29879                 for(var i = 0, len = rs.records.length; i < len; i++) {
29880                     var r = rs.records[i];
29881                     errors[i] = r.data;
29882                 }
29883             }
29884             if(errors.length < 1){
29885                 errors = null;
29886             }
29887             return {
29888                 success : rs.success,
29889                 errors : errors
29890             };
29891         }
29892         var ret = false;
29893         try {
29894             ret = Roo.decode(response.responseText);
29895         } catch (e) {
29896             ret = {
29897                 success: false,
29898                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29899                 errors : []
29900             };
29901         }
29902         return ret;
29903         
29904     }
29905 });
29906
29907
29908 Roo.form.Action.Load = function(form, options){
29909     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29910     this.reader = this.form.reader;
29911 };
29912
29913 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29914     type : 'load',
29915
29916     run : function(){
29917         
29918         Roo.Ajax.request(Roo.apply(
29919                 this.createCallback(), {
29920                     method:this.getMethod(),
29921                     url:this.getUrl(false),
29922                     params:this.getParams()
29923         }));
29924     },
29925
29926     success : function(response){
29927         
29928         var result = this.processResponse(response);
29929         if(result === true || !result.success || !result.data){
29930             this.failureType = Roo.form.Action.LOAD_FAILURE;
29931             this.form.afterAction(this, false);
29932             return;
29933         }
29934         this.form.clearInvalid();
29935         this.form.setValues(result.data);
29936         this.form.afterAction(this, true);
29937     },
29938
29939     handleResponse : function(response){
29940         if(this.form.reader){
29941             var rs = this.form.reader.read(response);
29942             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29943             return {
29944                 success : rs.success,
29945                 data : data
29946             };
29947         }
29948         return Roo.decode(response.responseText);
29949     }
29950 });
29951
29952 Roo.form.Action.ACTION_TYPES = {
29953     'load' : Roo.form.Action.Load,
29954     'submit' : Roo.form.Action.Submit
29955 };/*
29956  * Based on:
29957  * Ext JS Library 1.1.1
29958  * Copyright(c) 2006-2007, Ext JS, LLC.
29959  *
29960  * Originally Released Under LGPL - original licence link has changed is not relivant.
29961  *
29962  * Fork - LGPL
29963  * <script type="text/javascript">
29964  */
29965  
29966 /**
29967  * @class Roo.form.Layout
29968  * @extends Roo.Component
29969  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29970  * @constructor
29971  * @param {Object} config Configuration options
29972  */
29973 Roo.form.Layout = function(config){
29974     var xitems = [];
29975     if (config.items) {
29976         xitems = config.items;
29977         delete config.items;
29978     }
29979     Roo.form.Layout.superclass.constructor.call(this, config);
29980     this.stack = [];
29981     Roo.each(xitems, this.addxtype, this);
29982      
29983 };
29984
29985 Roo.extend(Roo.form.Layout, Roo.Component, {
29986     /**
29987      * @cfg {String/Object} autoCreate
29988      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29989      */
29990     /**
29991      * @cfg {String/Object/Function} style
29992      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29993      * a function which returns such a specification.
29994      */
29995     /**
29996      * @cfg {String} labelAlign
29997      * Valid values are "left," "top" and "right" (defaults to "left")
29998      */
29999     /**
30000      * @cfg {Number} labelWidth
30001      * Fixed width in pixels of all field labels (defaults to undefined)
30002      */
30003     /**
30004      * @cfg {Boolean} clear
30005      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30006      */
30007     clear : true,
30008     /**
30009      * @cfg {String} labelSeparator
30010      * The separator to use after field labels (defaults to ':')
30011      */
30012     labelSeparator : ':',
30013     /**
30014      * @cfg {Boolean} hideLabels
30015      * True to suppress the display of field labels in this layout (defaults to false)
30016      */
30017     hideLabels : false,
30018
30019     // private
30020     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30021     
30022     isLayout : true,
30023     
30024     // private
30025     onRender : function(ct, position){
30026         if(this.el){ // from markup
30027             this.el = Roo.get(this.el);
30028         }else {  // generate
30029             var cfg = this.getAutoCreate();
30030             this.el = ct.createChild(cfg, position);
30031         }
30032         if(this.style){
30033             this.el.applyStyles(this.style);
30034         }
30035         if(this.labelAlign){
30036             this.el.addClass('x-form-label-'+this.labelAlign);
30037         }
30038         if(this.hideLabels){
30039             this.labelStyle = "display:none";
30040             this.elementStyle = "padding-left:0;";
30041         }else{
30042             if(typeof this.labelWidth == 'number'){
30043                 this.labelStyle = "width:"+this.labelWidth+"px;";
30044                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30045             }
30046             if(this.labelAlign == 'top'){
30047                 this.labelStyle = "width:auto;";
30048                 this.elementStyle = "padding-left:0;";
30049             }
30050         }
30051         var stack = this.stack;
30052         var slen = stack.length;
30053         if(slen > 0){
30054             if(!this.fieldTpl){
30055                 var t = new Roo.Template(
30056                     '<div class="x-form-item {5}">',
30057                         '<label for="{0}" style="{2}">{1}{4}</label>',
30058                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30059                         '</div>',
30060                     '</div><div class="x-form-clear-left"></div>'
30061                 );
30062                 t.disableFormats = true;
30063                 t.compile();
30064                 Roo.form.Layout.prototype.fieldTpl = t;
30065             }
30066             for(var i = 0; i < slen; i++) {
30067                 if(stack[i].isFormField){
30068                     this.renderField(stack[i]);
30069                 }else{
30070                     this.renderComponent(stack[i]);
30071                 }
30072             }
30073         }
30074         if(this.clear){
30075             this.el.createChild({cls:'x-form-clear'});
30076         }
30077     },
30078
30079     // private
30080     renderField : function(f){
30081         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30082                f.id, //0
30083                f.fieldLabel, //1
30084                f.labelStyle||this.labelStyle||'', //2
30085                this.elementStyle||'', //3
30086                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30087                f.itemCls||this.itemCls||''  //5
30088        ], true).getPrevSibling());
30089     },
30090
30091     // private
30092     renderComponent : function(c){
30093         c.render(c.isLayout ? this.el : this.el.createChild());    
30094     },
30095     /**
30096      * Adds a object form elements (using the xtype property as the factory method.)
30097      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30098      * @param {Object} config 
30099      */
30100     addxtype : function(o)
30101     {
30102         // create the lement.
30103         o.form = this.form;
30104         var fe = Roo.factory(o, Roo.form);
30105         this.form.allItems.push(fe);
30106         this.stack.push(fe);
30107         
30108         if (fe.isFormField) {
30109             this.form.items.add(fe);
30110         }
30111          
30112         return fe;
30113     }
30114 });
30115
30116 /**
30117  * @class Roo.form.Column
30118  * @extends Roo.form.Layout
30119  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30120  * @constructor
30121  * @param {Object} config Configuration options
30122  */
30123 Roo.form.Column = function(config){
30124     Roo.form.Column.superclass.constructor.call(this, config);
30125 };
30126
30127 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30128     /**
30129      * @cfg {Number/String} width
30130      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30131      */
30132     /**
30133      * @cfg {String/Object} autoCreate
30134      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30135      */
30136
30137     // private
30138     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30139
30140     // private
30141     onRender : function(ct, position){
30142         Roo.form.Column.superclass.onRender.call(this, ct, position);
30143         if(this.width){
30144             this.el.setWidth(this.width);
30145         }
30146     }
30147 });
30148
30149
30150 /**
30151  * @class Roo.form.Row
30152  * @extends Roo.form.Layout
30153  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30154  * @constructor
30155  * @param {Object} config Configuration options
30156  */
30157
30158  
30159 Roo.form.Row = function(config){
30160     Roo.form.Row.superclass.constructor.call(this, config);
30161 };
30162  
30163 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30164       /**
30165      * @cfg {Number/String} width
30166      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30167      */
30168     /**
30169      * @cfg {Number/String} height
30170      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30171      */
30172     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30173     
30174     padWidth : 20,
30175     // private
30176     onRender : function(ct, position){
30177         //console.log('row render');
30178         if(!this.rowTpl){
30179             var t = new Roo.Template(
30180                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30181                     '<label for="{0}" style="{2}">{1}{4}</label>',
30182                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30183                     '</div>',
30184                 '</div>'
30185             );
30186             t.disableFormats = true;
30187             t.compile();
30188             Roo.form.Layout.prototype.rowTpl = t;
30189         }
30190         this.fieldTpl = this.rowTpl;
30191         
30192         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30193         var labelWidth = 100;
30194         
30195         if ((this.labelAlign != 'top')) {
30196             if (typeof this.labelWidth == 'number') {
30197                 labelWidth = this.labelWidth
30198             }
30199             this.padWidth =  20 + labelWidth;
30200             
30201         }
30202         
30203         Roo.form.Column.superclass.onRender.call(this, ct, position);
30204         if(this.width){
30205             this.el.setWidth(this.width);
30206         }
30207         if(this.height){
30208             this.el.setHeight(this.height);
30209         }
30210     },
30211     
30212     // private
30213     renderField : function(f){
30214         f.fieldEl = this.fieldTpl.append(this.el, [
30215                f.id, f.fieldLabel,
30216                f.labelStyle||this.labelStyle||'',
30217                this.elementStyle||'',
30218                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30219                f.itemCls||this.itemCls||'',
30220                f.width ? f.width + this.padWidth : 160 + this.padWidth
30221        ],true);
30222     }
30223 });
30224  
30225
30226 /**
30227  * @class Roo.form.FieldSet
30228  * @extends Roo.form.Layout
30229  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30230  * @constructor
30231  * @param {Object} config Configuration options
30232  */
30233 Roo.form.FieldSet = function(config){
30234     Roo.form.FieldSet.superclass.constructor.call(this, config);
30235 };
30236
30237 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30238     /**
30239      * @cfg {String} legend
30240      * The text to display as the legend for the FieldSet (defaults to '')
30241      */
30242     /**
30243      * @cfg {String/Object} autoCreate
30244      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30245      */
30246
30247     // private
30248     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30249
30250     // private
30251     onRender : function(ct, position){
30252         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30253         if(this.legend){
30254             this.setLegend(this.legend);
30255         }
30256     },
30257
30258     // private
30259     setLegend : function(text){
30260         if(this.rendered){
30261             this.el.child('legend').update(text);
30262         }
30263     }
30264 });/*
30265  * Based on:
30266  * Ext JS Library 1.1.1
30267  * Copyright(c) 2006-2007, Ext JS, LLC.
30268  *
30269  * Originally Released Under LGPL - original licence link has changed is not relivant.
30270  *
30271  * Fork - LGPL
30272  * <script type="text/javascript">
30273  */
30274 /**
30275  * @class Roo.form.VTypes
30276  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30277  * @singleton
30278  */
30279 Roo.form.VTypes = function(){
30280     // closure these in so they are only created once.
30281     var alpha = /^[a-zA-Z_]+$/;
30282     var alphanum = /^[a-zA-Z0-9_]+$/;
30283     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
30284     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30285
30286     // All these messages and functions are configurable
30287     return {
30288         /**
30289          * The function used to validate email addresses
30290          * @param {String} value The email address
30291          */
30292         'email' : function(v){
30293             return email.test(v);
30294         },
30295         /**
30296          * The error text to display when the email validation function returns false
30297          * @type String
30298          */
30299         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30300         /**
30301          * The keystroke filter mask to be applied on email input
30302          * @type RegExp
30303          */
30304         'emailMask' : /[a-z0-9_\.\-@]/i,
30305
30306         /**
30307          * The function used to validate URLs
30308          * @param {String} value The URL
30309          */
30310         'url' : function(v){
30311             return url.test(v);
30312         },
30313         /**
30314          * The error text to display when the url validation function returns false
30315          * @type String
30316          */
30317         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30318         
30319         /**
30320          * The function used to validate alpha values
30321          * @param {String} value The value
30322          */
30323         'alpha' : function(v){
30324             return alpha.test(v);
30325         },
30326         /**
30327          * The error text to display when the alpha validation function returns false
30328          * @type String
30329          */
30330         'alphaText' : 'This field should only contain letters and _',
30331         /**
30332          * The keystroke filter mask to be applied on alpha input
30333          * @type RegExp
30334          */
30335         'alphaMask' : /[a-z_]/i,
30336
30337         /**
30338          * The function used to validate alphanumeric values
30339          * @param {String} value The value
30340          */
30341         'alphanum' : function(v){
30342             return alphanum.test(v);
30343         },
30344         /**
30345          * The error text to display when the alphanumeric validation function returns false
30346          * @type String
30347          */
30348         'alphanumText' : 'This field should only contain letters, numbers and _',
30349         /**
30350          * The keystroke filter mask to be applied on alphanumeric input
30351          * @type RegExp
30352          */
30353         'alphanumMask' : /[a-z0-9_]/i
30354     };
30355 }();//<script type="text/javascript">
30356
30357 /**
30358  * @class Roo.form.FCKeditor
30359  * @extends Roo.form.TextArea
30360  * Wrapper around the FCKEditor http://www.fckeditor.net
30361  * @constructor
30362  * Creates a new FCKeditor
30363  * @param {Object} config Configuration options
30364  */
30365 Roo.form.FCKeditor = function(config){
30366     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30367     this.addEvents({
30368          /**
30369          * @event editorinit
30370          * Fired when the editor is initialized - you can add extra handlers here..
30371          * @param {FCKeditor} this
30372          * @param {Object} the FCK object.
30373          */
30374         editorinit : true
30375     });
30376     
30377     
30378 };
30379 Roo.form.FCKeditor.editors = { };
30380 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30381 {
30382     //defaultAutoCreate : {
30383     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30384     //},
30385     // private
30386     /**
30387      * @cfg {Object} fck options - see fck manual for details.
30388      */
30389     fckconfig : false,
30390     
30391     /**
30392      * @cfg {Object} fck toolbar set (Basic or Default)
30393      */
30394     toolbarSet : 'Basic',
30395     /**
30396      * @cfg {Object} fck BasePath
30397      */ 
30398     basePath : '/fckeditor/',
30399     
30400     
30401     frame : false,
30402     
30403     value : '',
30404     
30405    
30406     onRender : function(ct, position)
30407     {
30408         if(!this.el){
30409             this.defaultAutoCreate = {
30410                 tag: "textarea",
30411                 style:"width:300px;height:60px;",
30412                 autocomplete: "new-password"
30413             };
30414         }
30415         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30416         /*
30417         if(this.grow){
30418             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30419             if(this.preventScrollbars){
30420                 this.el.setStyle("overflow", "hidden");
30421             }
30422             this.el.setHeight(this.growMin);
30423         }
30424         */
30425         //console.log('onrender' + this.getId() );
30426         Roo.form.FCKeditor.editors[this.getId()] = this;
30427          
30428
30429         this.replaceTextarea() ;
30430         
30431     },
30432     
30433     getEditor : function() {
30434         return this.fckEditor;
30435     },
30436     /**
30437      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30438      * @param {Mixed} value The value to set
30439      */
30440     
30441     
30442     setValue : function(value)
30443     {
30444         //console.log('setValue: ' + value);
30445         
30446         if(typeof(value) == 'undefined') { // not sure why this is happending...
30447             return;
30448         }
30449         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30450         
30451         //if(!this.el || !this.getEditor()) {
30452         //    this.value = value;
30453             //this.setValue.defer(100,this,[value]);    
30454         //    return;
30455         //} 
30456         
30457         if(!this.getEditor()) {
30458             return;
30459         }
30460         
30461         this.getEditor().SetData(value);
30462         
30463         //
30464
30465     },
30466
30467     /**
30468      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30469      * @return {Mixed} value The field value
30470      */
30471     getValue : function()
30472     {
30473         
30474         if (this.frame && this.frame.dom.style.display == 'none') {
30475             return Roo.form.FCKeditor.superclass.getValue.call(this);
30476         }
30477         
30478         if(!this.el || !this.getEditor()) {
30479            
30480            // this.getValue.defer(100,this); 
30481             return this.value;
30482         }
30483        
30484         
30485         var value=this.getEditor().GetData();
30486         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30487         return Roo.form.FCKeditor.superclass.getValue.call(this);
30488         
30489
30490     },
30491
30492     /**
30493      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30494      * @return {Mixed} value The field value
30495      */
30496     getRawValue : function()
30497     {
30498         if (this.frame && this.frame.dom.style.display == 'none') {
30499             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30500         }
30501         
30502         if(!this.el || !this.getEditor()) {
30503             //this.getRawValue.defer(100,this); 
30504             return this.value;
30505             return;
30506         }
30507         
30508         
30509         
30510         var value=this.getEditor().GetData();
30511         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30512         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30513          
30514     },
30515     
30516     setSize : function(w,h) {
30517         
30518         
30519         
30520         //if (this.frame && this.frame.dom.style.display == 'none') {
30521         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30522         //    return;
30523         //}
30524         //if(!this.el || !this.getEditor()) {
30525         //    this.setSize.defer(100,this, [w,h]); 
30526         //    return;
30527         //}
30528         
30529         
30530         
30531         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30532         
30533         this.frame.dom.setAttribute('width', w);
30534         this.frame.dom.setAttribute('height', h);
30535         this.frame.setSize(w,h);
30536         
30537     },
30538     
30539     toggleSourceEdit : function(value) {
30540         
30541       
30542          
30543         this.el.dom.style.display = value ? '' : 'none';
30544         this.frame.dom.style.display = value ?  'none' : '';
30545         
30546     },
30547     
30548     
30549     focus: function(tag)
30550     {
30551         if (this.frame.dom.style.display == 'none') {
30552             return Roo.form.FCKeditor.superclass.focus.call(this);
30553         }
30554         if(!this.el || !this.getEditor()) {
30555             this.focus.defer(100,this, [tag]); 
30556             return;
30557         }
30558         
30559         
30560         
30561         
30562         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30563         this.getEditor().Focus();
30564         if (tgs.length) {
30565             if (!this.getEditor().Selection.GetSelection()) {
30566                 this.focus.defer(100,this, [tag]); 
30567                 return;
30568             }
30569             
30570             
30571             var r = this.getEditor().EditorDocument.createRange();
30572             r.setStart(tgs[0],0);
30573             r.setEnd(tgs[0],0);
30574             this.getEditor().Selection.GetSelection().removeAllRanges();
30575             this.getEditor().Selection.GetSelection().addRange(r);
30576             this.getEditor().Focus();
30577         }
30578         
30579     },
30580     
30581     
30582     
30583     replaceTextarea : function()
30584     {
30585         if ( document.getElementById( this.getId() + '___Frame' ) ) {
30586             return ;
30587         }
30588         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30589         //{
30590             // We must check the elements firstly using the Id and then the name.
30591         var oTextarea = document.getElementById( this.getId() );
30592         
30593         var colElementsByName = document.getElementsByName( this.getId() ) ;
30594          
30595         oTextarea.style.display = 'none' ;
30596
30597         if ( oTextarea.tabIndex ) {            
30598             this.TabIndex = oTextarea.tabIndex ;
30599         }
30600         
30601         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30602         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30603         this.frame = Roo.get(this.getId() + '___Frame')
30604     },
30605     
30606     _getConfigHtml : function()
30607     {
30608         var sConfig = '' ;
30609
30610         for ( var o in this.fckconfig ) {
30611             sConfig += sConfig.length > 0  ? '&amp;' : '';
30612             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30613         }
30614
30615         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30616     },
30617     
30618     
30619     _getIFrameHtml : function()
30620     {
30621         var sFile = 'fckeditor.html' ;
30622         /* no idea what this is about..
30623         try
30624         {
30625             if ( (/fcksource=true/i).test( window.top.location.search ) )
30626                 sFile = 'fckeditor.original.html' ;
30627         }
30628         catch (e) { 
30629         */
30630
30631         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30632         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30633         
30634         
30635         var html = '<iframe id="' + this.getId() +
30636             '___Frame" src="' + sLink +
30637             '" width="' + this.width +
30638             '" height="' + this.height + '"' +
30639             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30640             ' frameborder="0" scrolling="no"></iframe>' ;
30641
30642         return html ;
30643     },
30644     
30645     _insertHtmlBefore : function( html, element )
30646     {
30647         if ( element.insertAdjacentHTML )       {
30648             // IE
30649             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30650         } else { // Gecko
30651             var oRange = document.createRange() ;
30652             oRange.setStartBefore( element ) ;
30653             var oFragment = oRange.createContextualFragment( html );
30654             element.parentNode.insertBefore( oFragment, element ) ;
30655         }
30656     }
30657     
30658     
30659   
30660     
30661     
30662     
30663     
30664
30665 });
30666
30667 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30668
30669 function FCKeditor_OnComplete(editorInstance){
30670     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30671     f.fckEditor = editorInstance;
30672     //console.log("loaded");
30673     f.fireEvent('editorinit', f, editorInstance);
30674
30675   
30676
30677  
30678
30679
30680
30681
30682
30683
30684
30685
30686
30687
30688
30689
30690
30691
30692
30693 //<script type="text/javascript">
30694 /**
30695  * @class Roo.form.GridField
30696  * @extends Roo.form.Field
30697  * Embed a grid (or editable grid into a form)
30698  * STATUS ALPHA
30699  * 
30700  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30701  * it needs 
30702  * xgrid.store = Roo.data.Store
30703  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30704  * xgrid.store.reader = Roo.data.JsonReader 
30705  * 
30706  * 
30707  * @constructor
30708  * Creates a new GridField
30709  * @param {Object} config Configuration options
30710  */
30711 Roo.form.GridField = function(config){
30712     Roo.form.GridField.superclass.constructor.call(this, config);
30713      
30714 };
30715
30716 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30717     /**
30718      * @cfg {Number} width  - used to restrict width of grid..
30719      */
30720     width : 100,
30721     /**
30722      * @cfg {Number} height - used to restrict height of grid..
30723      */
30724     height : 50,
30725      /**
30726      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30727          * 
30728          *}
30729      */
30730     xgrid : false, 
30731     /**
30732      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30733      * {tag: "input", type: "checkbox", autocomplete: "off"})
30734      */
30735    // defaultAutoCreate : { tag: 'div' },
30736     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30737     /**
30738      * @cfg {String} addTitle Text to include for adding a title.
30739      */
30740     addTitle : false,
30741     //
30742     onResize : function(){
30743         Roo.form.Field.superclass.onResize.apply(this, arguments);
30744     },
30745
30746     initEvents : function(){
30747         // Roo.form.Checkbox.superclass.initEvents.call(this);
30748         // has no events...
30749        
30750     },
30751
30752
30753     getResizeEl : function(){
30754         return this.wrap;
30755     },
30756
30757     getPositionEl : function(){
30758         return this.wrap;
30759     },
30760
30761     // private
30762     onRender : function(ct, position){
30763         
30764         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30765         var style = this.style;
30766         delete this.style;
30767         
30768         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30769         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30770         this.viewEl = this.wrap.createChild({ tag: 'div' });
30771         if (style) {
30772             this.viewEl.applyStyles(style);
30773         }
30774         if (this.width) {
30775             this.viewEl.setWidth(this.width);
30776         }
30777         if (this.height) {
30778             this.viewEl.setHeight(this.height);
30779         }
30780         //if(this.inputValue !== undefined){
30781         //this.setValue(this.value);
30782         
30783         
30784         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30785         
30786         
30787         this.grid.render();
30788         this.grid.getDataSource().on('remove', this.refreshValue, this);
30789         this.grid.getDataSource().on('update', this.refreshValue, this);
30790         this.grid.on('afteredit', this.refreshValue, this);
30791  
30792     },
30793      
30794     
30795     /**
30796      * Sets the value of the item. 
30797      * @param {String} either an object  or a string..
30798      */
30799     setValue : function(v){
30800         //this.value = v;
30801         v = v || []; // empty set..
30802         // this does not seem smart - it really only affects memoryproxy grids..
30803         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30804             var ds = this.grid.getDataSource();
30805             // assumes a json reader..
30806             var data = {}
30807             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30808             ds.loadData( data);
30809         }
30810         // clear selection so it does not get stale.
30811         if (this.grid.sm) { 
30812             this.grid.sm.clearSelections();
30813         }
30814         
30815         Roo.form.GridField.superclass.setValue.call(this, v);
30816         this.refreshValue();
30817         // should load data in the grid really....
30818     },
30819     
30820     // private
30821     refreshValue: function() {
30822          var val = [];
30823         this.grid.getDataSource().each(function(r) {
30824             val.push(r.data);
30825         });
30826         this.el.dom.value = Roo.encode(val);
30827     }
30828     
30829      
30830     
30831     
30832 });/*
30833  * Based on:
30834  * Ext JS Library 1.1.1
30835  * Copyright(c) 2006-2007, Ext JS, LLC.
30836  *
30837  * Originally Released Under LGPL - original licence link has changed is not relivant.
30838  *
30839  * Fork - LGPL
30840  * <script type="text/javascript">
30841  */
30842 /**
30843  * @class Roo.form.DisplayField
30844  * @extends Roo.form.Field
30845  * A generic Field to display non-editable data.
30846  * @cfg {Boolean} closable (true|false) default false
30847  * @constructor
30848  * Creates a new Display Field item.
30849  * @param {Object} config Configuration options
30850  */
30851 Roo.form.DisplayField = function(config){
30852     Roo.form.DisplayField.superclass.constructor.call(this, config);
30853     
30854     this.addEvents({
30855         /**
30856          * @event close
30857          * Fires after the click the close btn
30858              * @param {Roo.form.DisplayField} this
30859              */
30860         close : true
30861     });
30862 };
30863
30864 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30865     inputType:      'hidden',
30866     allowBlank:     true,
30867     readOnly:         true,
30868     
30869  
30870     /**
30871      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30872      */
30873     focusClass : undefined,
30874     /**
30875      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30876      */
30877     fieldClass: 'x-form-field',
30878     
30879      /**
30880      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30881      */
30882     valueRenderer: undefined,
30883     
30884     width: 100,
30885     /**
30886      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30887      * {tag: "input", type: "checkbox", autocomplete: "off"})
30888      */
30889      
30890  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30891  
30892     closable : false,
30893     
30894     onResize : function(){
30895         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30896         
30897     },
30898
30899     initEvents : function(){
30900         // Roo.form.Checkbox.superclass.initEvents.call(this);
30901         // has no events...
30902         
30903         if(this.closable){
30904             this.closeEl.on('click', this.onClose, this);
30905         }
30906        
30907     },
30908
30909
30910     getResizeEl : function(){
30911         return this.wrap;
30912     },
30913
30914     getPositionEl : function(){
30915         return this.wrap;
30916     },
30917
30918     // private
30919     onRender : function(ct, position){
30920         
30921         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30922         //if(this.inputValue !== undefined){
30923         this.wrap = this.el.wrap();
30924         
30925         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30926         
30927         if(this.closable){
30928             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
30929         }
30930         
30931         if (this.bodyStyle) {
30932             this.viewEl.applyStyles(this.bodyStyle);
30933         }
30934         //this.viewEl.setStyle('padding', '2px');
30935         
30936         this.setValue(this.value);
30937         
30938     },
30939 /*
30940     // private
30941     initValue : Roo.emptyFn,
30942
30943   */
30944
30945         // private
30946     onClick : function(){
30947         
30948     },
30949
30950     /**
30951      * Sets the checked state of the checkbox.
30952      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30953      */
30954     setValue : function(v){
30955         this.value = v;
30956         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30957         // this might be called before we have a dom element..
30958         if (!this.viewEl) {
30959             return;
30960         }
30961         this.viewEl.dom.innerHTML = html;
30962         Roo.form.DisplayField.superclass.setValue.call(this, v);
30963
30964     },
30965     
30966     onClose : function(e)
30967     {
30968         e.preventDefault();
30969         
30970         this.fireEvent('close', this);
30971     }
30972 });/*
30973  * 
30974  * Licence- LGPL
30975  * 
30976  */
30977
30978 /**
30979  * @class Roo.form.DayPicker
30980  * @extends Roo.form.Field
30981  * A Day picker show [M] [T] [W] ....
30982  * @constructor
30983  * Creates a new Day Picker
30984  * @param {Object} config Configuration options
30985  */
30986 Roo.form.DayPicker= function(config){
30987     Roo.form.DayPicker.superclass.constructor.call(this, config);
30988      
30989 };
30990
30991 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30992     /**
30993      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30994      */
30995     focusClass : undefined,
30996     /**
30997      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30998      */
30999     fieldClass: "x-form-field",
31000    
31001     /**
31002      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31003      * {tag: "input", type: "checkbox", autocomplete: "off"})
31004      */
31005     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31006     
31007    
31008     actionMode : 'viewEl', 
31009     //
31010     // private
31011  
31012     inputType : 'hidden',
31013     
31014      
31015     inputElement: false, // real input element?
31016     basedOn: false, // ????
31017     
31018     isFormField: true, // not sure where this is needed!!!!
31019
31020     onResize : function(){
31021         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31022         if(!this.boxLabel){
31023             this.el.alignTo(this.wrap, 'c-c');
31024         }
31025     },
31026
31027     initEvents : function(){
31028         Roo.form.Checkbox.superclass.initEvents.call(this);
31029         this.el.on("click", this.onClick,  this);
31030         this.el.on("change", this.onClick,  this);
31031     },
31032
31033
31034     getResizeEl : function(){
31035         return this.wrap;
31036     },
31037
31038     getPositionEl : function(){
31039         return this.wrap;
31040     },
31041
31042     
31043     // private
31044     onRender : function(ct, position){
31045         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31046        
31047         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31048         
31049         var r1 = '<table><tr>';
31050         var r2 = '<tr class="x-form-daypick-icons">';
31051         for (var i=0; i < 7; i++) {
31052             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31053             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31054         }
31055         
31056         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31057         viewEl.select('img').on('click', this.onClick, this);
31058         this.viewEl = viewEl;   
31059         
31060         
31061         // this will not work on Chrome!!!
31062         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31063         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31064         
31065         
31066           
31067
31068     },
31069
31070     // private
31071     initValue : Roo.emptyFn,
31072
31073     /**
31074      * Returns the checked state of the checkbox.
31075      * @return {Boolean} True if checked, else false
31076      */
31077     getValue : function(){
31078         return this.el.dom.value;
31079         
31080     },
31081
31082         // private
31083     onClick : function(e){ 
31084         //this.setChecked(!this.checked);
31085         Roo.get(e.target).toggleClass('x-menu-item-checked');
31086         this.refreshValue();
31087         //if(this.el.dom.checked != this.checked){
31088         //    this.setValue(this.el.dom.checked);
31089        // }
31090     },
31091     
31092     // private
31093     refreshValue : function()
31094     {
31095         var val = '';
31096         this.viewEl.select('img',true).each(function(e,i,n)  {
31097             val += e.is(".x-menu-item-checked") ? String(n) : '';
31098         });
31099         this.setValue(val, true);
31100     },
31101
31102     /**
31103      * Sets the checked state of the checkbox.
31104      * On is always based on a string comparison between inputValue and the param.
31105      * @param {Boolean/String} value - the value to set 
31106      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31107      */
31108     setValue : function(v,suppressEvent){
31109         if (!this.el.dom) {
31110             return;
31111         }
31112         var old = this.el.dom.value ;
31113         this.el.dom.value = v;
31114         if (suppressEvent) {
31115             return ;
31116         }
31117          
31118         // update display..
31119         this.viewEl.select('img',true).each(function(e,i,n)  {
31120             
31121             var on = e.is(".x-menu-item-checked");
31122             var newv = v.indexOf(String(n)) > -1;
31123             if (on != newv) {
31124                 e.toggleClass('x-menu-item-checked');
31125             }
31126             
31127         });
31128         
31129         
31130         this.fireEvent('change', this, v, old);
31131         
31132         
31133     },
31134    
31135     // handle setting of hidden value by some other method!!?!?
31136     setFromHidden: function()
31137     {
31138         if(!this.el){
31139             return;
31140         }
31141         //console.log("SET FROM HIDDEN");
31142         //alert('setFrom hidden');
31143         this.setValue(this.el.dom.value);
31144     },
31145     
31146     onDestroy : function()
31147     {
31148         if(this.viewEl){
31149             Roo.get(this.viewEl).remove();
31150         }
31151          
31152         Roo.form.DayPicker.superclass.onDestroy.call(this);
31153     }
31154
31155 });/*
31156  * RooJS Library 1.1.1
31157  * Copyright(c) 2008-2011  Alan Knowles
31158  *
31159  * License - LGPL
31160  */
31161  
31162
31163 /**
31164  * @class Roo.form.ComboCheck
31165  * @extends Roo.form.ComboBox
31166  * A combobox for multiple select items.
31167  *
31168  * FIXME - could do with a reset button..
31169  * 
31170  * @constructor
31171  * Create a new ComboCheck
31172  * @param {Object} config Configuration options
31173  */
31174 Roo.form.ComboCheck = function(config){
31175     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31176     // should verify some data...
31177     // like
31178     // hiddenName = required..
31179     // displayField = required
31180     // valudField == required
31181     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31182     var _t = this;
31183     Roo.each(req, function(e) {
31184         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31185             throw "Roo.form.ComboCheck : missing value for: " + e;
31186         }
31187     });
31188     
31189     
31190 };
31191
31192 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31193      
31194      
31195     editable : false,
31196      
31197     selectedClass: 'x-menu-item-checked', 
31198     
31199     // private
31200     onRender : function(ct, position){
31201         var _t = this;
31202         
31203         
31204         
31205         if(!this.tpl){
31206             var cls = 'x-combo-list';
31207
31208             
31209             this.tpl =  new Roo.Template({
31210                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31211                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31212                    '<span>{' + this.displayField + '}</span>' +
31213                     '</div>' 
31214                 
31215             });
31216         }
31217  
31218         
31219         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31220         this.view.singleSelect = false;
31221         this.view.multiSelect = true;
31222         this.view.toggleSelect = true;
31223         this.pageTb.add(new Roo.Toolbar.Fill(), {
31224             
31225             text: 'Done',
31226             handler: function()
31227             {
31228                 _t.collapse();
31229             }
31230         });
31231     },
31232     
31233     onViewOver : function(e, t){
31234         // do nothing...
31235         return;
31236         
31237     },
31238     
31239     onViewClick : function(doFocus,index){
31240         return;
31241         
31242     },
31243     select: function () {
31244         //Roo.log("SELECT CALLED");
31245     },
31246      
31247     selectByValue : function(xv, scrollIntoView){
31248         var ar = this.getValueArray();
31249         var sels = [];
31250         
31251         Roo.each(ar, function(v) {
31252             if(v === undefined || v === null){
31253                 return;
31254             }
31255             var r = this.findRecord(this.valueField, v);
31256             if(r){
31257                 sels.push(this.store.indexOf(r))
31258                 
31259             }
31260         },this);
31261         this.view.select(sels);
31262         return false;
31263     },
31264     
31265     
31266     
31267     onSelect : function(record, index){
31268        // Roo.log("onselect Called");
31269        // this is only called by the clear button now..
31270         this.view.clearSelections();
31271         this.setValue('[]');
31272         if (this.value != this.valueBefore) {
31273             this.fireEvent('change', this, this.value, this.valueBefore);
31274             this.valueBefore = this.value;
31275         }
31276     },
31277     getValueArray : function()
31278     {
31279         var ar = [] ;
31280         
31281         try {
31282             //Roo.log(this.value);
31283             if (typeof(this.value) == 'undefined') {
31284                 return [];
31285             }
31286             var ar = Roo.decode(this.value);
31287             return  ar instanceof Array ? ar : []; //?? valid?
31288             
31289         } catch(e) {
31290             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31291             return [];
31292         }
31293          
31294     },
31295     expand : function ()
31296     {
31297         
31298         Roo.form.ComboCheck.superclass.expand.call(this);
31299         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31300         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31301         
31302
31303     },
31304     
31305     collapse : function(){
31306         Roo.form.ComboCheck.superclass.collapse.call(this);
31307         var sl = this.view.getSelectedIndexes();
31308         var st = this.store;
31309         var nv = [];
31310         var tv = [];
31311         var r;
31312         Roo.each(sl, function(i) {
31313             r = st.getAt(i);
31314             nv.push(r.get(this.valueField));
31315         },this);
31316         this.setValue(Roo.encode(nv));
31317         if (this.value != this.valueBefore) {
31318
31319             this.fireEvent('change', this, this.value, this.valueBefore);
31320             this.valueBefore = this.value;
31321         }
31322         
31323     },
31324     
31325     setValue : function(v){
31326         // Roo.log(v);
31327         this.value = v;
31328         
31329         var vals = this.getValueArray();
31330         var tv = [];
31331         Roo.each(vals, function(k) {
31332             var r = this.findRecord(this.valueField, k);
31333             if(r){
31334                 tv.push(r.data[this.displayField]);
31335             }else if(this.valueNotFoundText !== undefined){
31336                 tv.push( this.valueNotFoundText );
31337             }
31338         },this);
31339        // Roo.log(tv);
31340         
31341         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31342         this.hiddenField.value = v;
31343         this.value = v;
31344     }
31345     
31346 });/*
31347  * Based on:
31348  * Ext JS Library 1.1.1
31349  * Copyright(c) 2006-2007, Ext JS, LLC.
31350  *
31351  * Originally Released Under LGPL - original licence link has changed is not relivant.
31352  *
31353  * Fork - LGPL
31354  * <script type="text/javascript">
31355  */
31356  
31357 /**
31358  * @class Roo.form.Signature
31359  * @extends Roo.form.Field
31360  * Signature field.  
31361  * @constructor
31362  * 
31363  * @param {Object} config Configuration options
31364  */
31365
31366 Roo.form.Signature = function(config){
31367     Roo.form.Signature.superclass.constructor.call(this, config);
31368     
31369     this.addEvents({// not in used??
31370          /**
31371          * @event confirm
31372          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31373              * @param {Roo.form.Signature} combo This combo box
31374              */
31375         'confirm' : true,
31376         /**
31377          * @event reset
31378          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31379              * @param {Roo.form.ComboBox} combo This combo box
31380              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31381              */
31382         'reset' : true
31383     });
31384 };
31385
31386 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31387     /**
31388      * @cfg {Object} labels Label to use when rendering a form.
31389      * defaults to 
31390      * labels : { 
31391      *      clear : "Clear",
31392      *      confirm : "Confirm"
31393      *  }
31394      */
31395     labels : { 
31396         clear : "Clear",
31397         confirm : "Confirm"
31398     },
31399     /**
31400      * @cfg {Number} width The signature panel width (defaults to 300)
31401      */
31402     width: 300,
31403     /**
31404      * @cfg {Number} height The signature panel height (defaults to 100)
31405      */
31406     height : 100,
31407     /**
31408      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31409      */
31410     allowBlank : false,
31411     
31412     //private
31413     // {Object} signPanel The signature SVG panel element (defaults to {})
31414     signPanel : {},
31415     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31416     isMouseDown : false,
31417     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31418     isConfirmed : false,
31419     // {String} signatureTmp SVG mapping string (defaults to empty string)
31420     signatureTmp : '',
31421     
31422     
31423     defaultAutoCreate : { // modified by initCompnoent..
31424         tag: "input",
31425         type:"hidden"
31426     },
31427
31428     // private
31429     onRender : function(ct, position){
31430         
31431         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31432         
31433         this.wrap = this.el.wrap({
31434             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31435         });
31436         
31437         this.createToolbar(this);
31438         this.signPanel = this.wrap.createChild({
31439                 tag: 'div',
31440                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31441             }, this.el
31442         );
31443             
31444         this.svgID = Roo.id();
31445         this.svgEl = this.signPanel.createChild({
31446               xmlns : 'http://www.w3.org/2000/svg',
31447               tag : 'svg',
31448               id : this.svgID + "-svg",
31449               width: this.width,
31450               height: this.height,
31451               viewBox: '0 0 '+this.width+' '+this.height,
31452               cn : [
31453                 {
31454                     tag: "rect",
31455                     id: this.svgID + "-svg-r",
31456                     width: this.width,
31457                     height: this.height,
31458                     fill: "#ffa"
31459                 },
31460                 {
31461                     tag: "line",
31462                     id: this.svgID + "-svg-l",
31463                     x1: "0", // start
31464                     y1: (this.height*0.8), // start set the line in 80% of height
31465                     x2: this.width, // end
31466                     y2: (this.height*0.8), // end set the line in 80% of height
31467                     'stroke': "#666",
31468                     'stroke-width': "1",
31469                     'stroke-dasharray': "3",
31470                     'shape-rendering': "crispEdges",
31471                     'pointer-events': "none"
31472                 },
31473                 {
31474                     tag: "path",
31475                     id: this.svgID + "-svg-p",
31476                     'stroke': "navy",
31477                     'stroke-width': "3",
31478                     'fill': "none",
31479                     'pointer-events': 'none'
31480                 }
31481               ]
31482         });
31483         this.createSVG();
31484         this.svgBox = this.svgEl.dom.getScreenCTM();
31485     },
31486     createSVG : function(){ 
31487         var svg = this.signPanel;
31488         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31489         var t = this;
31490
31491         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31492         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31493         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31494         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31495         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31496         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31497         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31498         
31499     },
31500     isTouchEvent : function(e){
31501         return e.type.match(/^touch/);
31502     },
31503     getCoords : function (e) {
31504         var pt    = this.svgEl.dom.createSVGPoint();
31505         pt.x = e.clientX; 
31506         pt.y = e.clientY;
31507         if (this.isTouchEvent(e)) {
31508             pt.x =  e.targetTouches[0].clientX;
31509             pt.y = e.targetTouches[0].clientY;
31510         }
31511         var a = this.svgEl.dom.getScreenCTM();
31512         var b = a.inverse();
31513         var mx = pt.matrixTransform(b);
31514         return mx.x + ',' + mx.y;
31515     },
31516     //mouse event headler 
31517     down : function (e) {
31518         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31519         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31520         
31521         this.isMouseDown = true;
31522         
31523         e.preventDefault();
31524     },
31525     move : function (e) {
31526         if (this.isMouseDown) {
31527             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31528             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31529         }
31530         
31531         e.preventDefault();
31532     },
31533     up : function (e) {
31534         this.isMouseDown = false;
31535         var sp = this.signatureTmp.split(' ');
31536         
31537         if(sp.length > 1){
31538             if(!sp[sp.length-2].match(/^L/)){
31539                 sp.pop();
31540                 sp.pop();
31541                 sp.push("");
31542                 this.signatureTmp = sp.join(" ");
31543             }
31544         }
31545         if(this.getValue() != this.signatureTmp){
31546             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31547             this.isConfirmed = false;
31548         }
31549         e.preventDefault();
31550     },
31551     
31552     /**
31553      * Protected method that will not generally be called directly. It
31554      * is called when the editor creates its toolbar. Override this method if you need to
31555      * add custom toolbar buttons.
31556      * @param {HtmlEditor} editor
31557      */
31558     createToolbar : function(editor){
31559          function btn(id, toggle, handler){
31560             var xid = fid + '-'+ id ;
31561             return {
31562                 id : xid,
31563                 cmd : id,
31564                 cls : 'x-btn-icon x-edit-'+id,
31565                 enableToggle:toggle !== false,
31566                 scope: editor, // was editor...
31567                 handler:handler||editor.relayBtnCmd,
31568                 clickEvent:'mousedown',
31569                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31570                 tabIndex:-1
31571             };
31572         }
31573         
31574         
31575         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31576         this.tb = tb;
31577         this.tb.add(
31578            {
31579                 cls : ' x-signature-btn x-signature-'+id,
31580                 scope: editor, // was editor...
31581                 handler: this.reset,
31582                 clickEvent:'mousedown',
31583                 text: this.labels.clear
31584             },
31585             {
31586                  xtype : 'Fill',
31587                  xns: Roo.Toolbar
31588             }, 
31589             {
31590                 cls : '  x-signature-btn x-signature-'+id,
31591                 scope: editor, // was editor...
31592                 handler: this.confirmHandler,
31593                 clickEvent:'mousedown',
31594                 text: this.labels.confirm
31595             }
31596         );
31597     
31598     },
31599     //public
31600     /**
31601      * when user is clicked confirm then show this image.....
31602      * 
31603      * @return {String} Image Data URI
31604      */
31605     getImageDataURI : function(){
31606         var svg = this.svgEl.dom.parentNode.innerHTML;
31607         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31608         return src; 
31609     },
31610     /**
31611      * 
31612      * @return {Boolean} this.isConfirmed
31613      */
31614     getConfirmed : function(){
31615         return this.isConfirmed;
31616     },
31617     /**
31618      * 
31619      * @return {Number} this.width
31620      */
31621     getWidth : function(){
31622         return this.width;
31623     },
31624     /**
31625      * 
31626      * @return {Number} this.height
31627      */
31628     getHeight : function(){
31629         return this.height;
31630     },
31631     // private
31632     getSignature : function(){
31633         return this.signatureTmp;
31634     },
31635     // private
31636     reset : function(){
31637         this.signatureTmp = '';
31638         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31639         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31640         this.isConfirmed = false;
31641         Roo.form.Signature.superclass.reset.call(this);
31642     },
31643     setSignature : function(s){
31644         this.signatureTmp = s;
31645         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31646         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31647         this.setValue(s);
31648         this.isConfirmed = false;
31649         Roo.form.Signature.superclass.reset.call(this);
31650     }, 
31651     test : function(){
31652 //        Roo.log(this.signPanel.dom.contentWindow.up())
31653     },
31654     //private
31655     setConfirmed : function(){
31656         
31657         
31658         
31659 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31660     },
31661     // private
31662     confirmHandler : function(){
31663         if(!this.getSignature()){
31664             return;
31665         }
31666         
31667         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31668         this.setValue(this.getSignature());
31669         this.isConfirmed = true;
31670         
31671         this.fireEvent('confirm', this);
31672     },
31673     // private
31674     // Subclasses should provide the validation implementation by overriding this
31675     validateValue : function(value){
31676         if(this.allowBlank){
31677             return true;
31678         }
31679         
31680         if(this.isConfirmed){
31681             return true;
31682         }
31683         return false;
31684     }
31685 });/*
31686  * Based on:
31687  * Ext JS Library 1.1.1
31688  * Copyright(c) 2006-2007, Ext JS, LLC.
31689  *
31690  * Originally Released Under LGPL - original licence link has changed is not relivant.
31691  *
31692  * Fork - LGPL
31693  * <script type="text/javascript">
31694  */
31695  
31696
31697 /**
31698  * @class Roo.form.ComboBox
31699  * @extends Roo.form.TriggerField
31700  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31701  * @constructor
31702  * Create a new ComboBox.
31703  * @param {Object} config Configuration options
31704  */
31705 Roo.form.Select = function(config){
31706     Roo.form.Select.superclass.constructor.call(this, config);
31707      
31708 };
31709
31710 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31711     /**
31712      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31713      */
31714     /**
31715      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31716      * rendering into an Roo.Editor, defaults to false)
31717      */
31718     /**
31719      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31720      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31721      */
31722     /**
31723      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31724      */
31725     /**
31726      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31727      * the dropdown list (defaults to undefined, with no header element)
31728      */
31729
31730      /**
31731      * @cfg {String/Roo.Template} tpl The template to use to render the output
31732      */
31733      
31734     // private
31735     defaultAutoCreate : {tag: "select"  },
31736     /**
31737      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31738      */
31739     listWidth: undefined,
31740     /**
31741      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31742      * mode = 'remote' or 'text' if mode = 'local')
31743      */
31744     displayField: undefined,
31745     /**
31746      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31747      * mode = 'remote' or 'value' if mode = 'local'). 
31748      * Note: use of a valueField requires the user make a selection
31749      * in order for a value to be mapped.
31750      */
31751     valueField: undefined,
31752     
31753     
31754     /**
31755      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31756      * field's data value (defaults to the underlying DOM element's name)
31757      */
31758     hiddenName: undefined,
31759     /**
31760      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31761      */
31762     listClass: '',
31763     /**
31764      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31765      */
31766     selectedClass: 'x-combo-selected',
31767     /**
31768      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31769      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31770      * which displays a downward arrow icon).
31771      */
31772     triggerClass : 'x-form-arrow-trigger',
31773     /**
31774      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31775      */
31776     shadow:'sides',
31777     /**
31778      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31779      * anchor positions (defaults to 'tl-bl')
31780      */
31781     listAlign: 'tl-bl?',
31782     /**
31783      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31784      */
31785     maxHeight: 300,
31786     /**
31787      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31788      * query specified by the allQuery config option (defaults to 'query')
31789      */
31790     triggerAction: 'query',
31791     /**
31792      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31793      * (defaults to 4, does not apply if editable = false)
31794      */
31795     minChars : 4,
31796     /**
31797      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31798      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31799      */
31800     typeAhead: false,
31801     /**
31802      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31803      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31804      */
31805     queryDelay: 500,
31806     /**
31807      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31808      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31809      */
31810     pageSize: 0,
31811     /**
31812      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31813      * when editable = true (defaults to false)
31814      */
31815     selectOnFocus:false,
31816     /**
31817      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31818      */
31819     queryParam: 'query',
31820     /**
31821      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31822      * when mode = 'remote' (defaults to 'Loading...')
31823      */
31824     loadingText: 'Loading...',
31825     /**
31826      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31827      */
31828     resizable: false,
31829     /**
31830      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31831      */
31832     handleHeight : 8,
31833     /**
31834      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31835      * traditional select (defaults to true)
31836      */
31837     editable: true,
31838     /**
31839      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31840      */
31841     allQuery: '',
31842     /**
31843      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31844      */
31845     mode: 'remote',
31846     /**
31847      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31848      * listWidth has a higher value)
31849      */
31850     minListWidth : 70,
31851     /**
31852      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31853      * allow the user to set arbitrary text into the field (defaults to false)
31854      */
31855     forceSelection:false,
31856     /**
31857      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31858      * if typeAhead = true (defaults to 250)
31859      */
31860     typeAheadDelay : 250,
31861     /**
31862      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31863      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31864      */
31865     valueNotFoundText : undefined,
31866     
31867     /**
31868      * @cfg {String} defaultValue The value displayed after loading the store.
31869      */
31870     defaultValue: '',
31871     
31872     /**
31873      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31874      */
31875     blockFocus : false,
31876     
31877     /**
31878      * @cfg {Boolean} disableClear Disable showing of clear button.
31879      */
31880     disableClear : false,
31881     /**
31882      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31883      */
31884     alwaysQuery : false,
31885     
31886     //private
31887     addicon : false,
31888     editicon: false,
31889     
31890     // element that contains real text value.. (when hidden is used..)
31891      
31892     // private
31893     onRender : function(ct, position){
31894         Roo.form.Field.prototype.onRender.call(this, ct, position);
31895         
31896         if(this.store){
31897             this.store.on('beforeload', this.onBeforeLoad, this);
31898             this.store.on('load', this.onLoad, this);
31899             this.store.on('loadexception', this.onLoadException, this);
31900             this.store.load({});
31901         }
31902         
31903         
31904         
31905     },
31906
31907     // private
31908     initEvents : function(){
31909         //Roo.form.ComboBox.superclass.initEvents.call(this);
31910  
31911     },
31912
31913     onDestroy : function(){
31914        
31915         if(this.store){
31916             this.store.un('beforeload', this.onBeforeLoad, this);
31917             this.store.un('load', this.onLoad, this);
31918             this.store.un('loadexception', this.onLoadException, this);
31919         }
31920         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31921     },
31922
31923     // private
31924     fireKey : function(e){
31925         if(e.isNavKeyPress() && !this.list.isVisible()){
31926             this.fireEvent("specialkey", this, e);
31927         }
31928     },
31929
31930     // private
31931     onResize: function(w, h){
31932         
31933         return; 
31934     
31935         
31936     },
31937
31938     /**
31939      * Allow or prevent the user from directly editing the field text.  If false is passed,
31940      * the user will only be able to select from the items defined in the dropdown list.  This method
31941      * is the runtime equivalent of setting the 'editable' config option at config time.
31942      * @param {Boolean} value True to allow the user to directly edit the field text
31943      */
31944     setEditable : function(value){
31945          
31946     },
31947
31948     // private
31949     onBeforeLoad : function(){
31950         
31951         Roo.log("Select before load");
31952         return;
31953     
31954         this.innerList.update(this.loadingText ?
31955                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31956         //this.restrictHeight();
31957         this.selectedIndex = -1;
31958     },
31959
31960     // private
31961     onLoad : function(){
31962
31963     
31964         var dom = this.el.dom;
31965         dom.innerHTML = '';
31966          var od = dom.ownerDocument;
31967          
31968         if (this.emptyText) {
31969             var op = od.createElement('option');
31970             op.setAttribute('value', '');
31971             op.innerHTML = String.format('{0}', this.emptyText);
31972             dom.appendChild(op);
31973         }
31974         if(this.store.getCount() > 0){
31975            
31976             var vf = this.valueField;
31977             var df = this.displayField;
31978             this.store.data.each(function(r) {
31979                 // which colmsn to use... testing - cdoe / title..
31980                 var op = od.createElement('option');
31981                 op.setAttribute('value', r.data[vf]);
31982                 op.innerHTML = String.format('{0}', r.data[df]);
31983                 dom.appendChild(op);
31984             });
31985             if (typeof(this.defaultValue != 'undefined')) {
31986                 this.setValue(this.defaultValue);
31987             }
31988             
31989              
31990         }else{
31991             //this.onEmptyResults();
31992         }
31993         //this.el.focus();
31994     },
31995     // private
31996     onLoadException : function()
31997     {
31998         dom.innerHTML = '';
31999             
32000         Roo.log("Select on load exception");
32001         return;
32002     
32003         this.collapse();
32004         Roo.log(this.store.reader.jsonData);
32005         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32006             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32007         }
32008         
32009         
32010     },
32011     // private
32012     onTypeAhead : function(){
32013          
32014     },
32015
32016     // private
32017     onSelect : function(record, index){
32018         Roo.log('on select?');
32019         return;
32020         if(this.fireEvent('beforeselect', this, record, index) !== false){
32021             this.setFromData(index > -1 ? record.data : false);
32022             this.collapse();
32023             this.fireEvent('select', this, record, index);
32024         }
32025     },
32026
32027     /**
32028      * Returns the currently selected field value or empty string if no value is set.
32029      * @return {String} value The selected value
32030      */
32031     getValue : function(){
32032         var dom = this.el.dom;
32033         this.value = dom.options[dom.selectedIndex].value;
32034         return this.value;
32035         
32036     },
32037
32038     /**
32039      * Clears any text/value currently set in the field
32040      */
32041     clearValue : function(){
32042         this.value = '';
32043         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32044         
32045     },
32046
32047     /**
32048      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32049      * will be displayed in the field.  If the value does not match the data value of an existing item,
32050      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32051      * Otherwise the field will be blank (although the value will still be set).
32052      * @param {String} value The value to match
32053      */
32054     setValue : function(v){
32055         var d = this.el.dom;
32056         for (var i =0; i < d.options.length;i++) {
32057             if (v == d.options[i].value) {
32058                 d.selectedIndex = i;
32059                 this.value = v;
32060                 return;
32061             }
32062         }
32063         this.clearValue();
32064     },
32065     /**
32066      * @property {Object} the last set data for the element
32067      */
32068     
32069     lastData : false,
32070     /**
32071      * Sets the value of the field based on a object which is related to the record format for the store.
32072      * @param {Object} value the value to set as. or false on reset?
32073      */
32074     setFromData : function(o){
32075         Roo.log('setfrom data?');
32076          
32077         
32078         
32079     },
32080     // private
32081     reset : function(){
32082         this.clearValue();
32083     },
32084     // private
32085     findRecord : function(prop, value){
32086         
32087         return false;
32088     
32089         var record;
32090         if(this.store.getCount() > 0){
32091             this.store.each(function(r){
32092                 if(r.data[prop] == value){
32093                     record = r;
32094                     return false;
32095                 }
32096                 return true;
32097             });
32098         }
32099         return record;
32100     },
32101     
32102     getName: function()
32103     {
32104         // returns hidden if it's set..
32105         if (!this.rendered) {return ''};
32106         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32107         
32108     },
32109      
32110
32111     
32112
32113     // private
32114     onEmptyResults : function(){
32115         Roo.log('empty results');
32116         //this.collapse();
32117     },
32118
32119     /**
32120      * Returns true if the dropdown list is expanded, else false.
32121      */
32122     isExpanded : function(){
32123         return false;
32124     },
32125
32126     /**
32127      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32128      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32129      * @param {String} value The data value of the item to select
32130      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32131      * selected item if it is not currently in view (defaults to true)
32132      * @return {Boolean} True if the value matched an item in the list, else false
32133      */
32134     selectByValue : function(v, scrollIntoView){
32135         Roo.log('select By Value');
32136         return false;
32137     
32138         if(v !== undefined && v !== null){
32139             var r = this.findRecord(this.valueField || this.displayField, v);
32140             if(r){
32141                 this.select(this.store.indexOf(r), scrollIntoView);
32142                 return true;
32143             }
32144         }
32145         return false;
32146     },
32147
32148     /**
32149      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32150      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32151      * @param {Number} index The zero-based index of the list item to select
32152      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32153      * selected item if it is not currently in view (defaults to true)
32154      */
32155     select : function(index, scrollIntoView){
32156         Roo.log('select ');
32157         return  ;
32158         
32159         this.selectedIndex = index;
32160         this.view.select(index);
32161         if(scrollIntoView !== false){
32162             var el = this.view.getNode(index);
32163             if(el){
32164                 this.innerList.scrollChildIntoView(el, false);
32165             }
32166         }
32167     },
32168
32169       
32170
32171     // private
32172     validateBlur : function(){
32173         
32174         return;
32175         
32176     },
32177
32178     // private
32179     initQuery : function(){
32180         this.doQuery(this.getRawValue());
32181     },
32182
32183     // private
32184     doForce : function(){
32185         if(this.el.dom.value.length > 0){
32186             this.el.dom.value =
32187                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32188              
32189         }
32190     },
32191
32192     /**
32193      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32194      * query allowing the query action to be canceled if needed.
32195      * @param {String} query The SQL query to execute
32196      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32197      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32198      * saved in the current store (defaults to false)
32199      */
32200     doQuery : function(q, forceAll){
32201         
32202         Roo.log('doQuery?');
32203         if(q === undefined || q === null){
32204             q = '';
32205         }
32206         var qe = {
32207             query: q,
32208             forceAll: forceAll,
32209             combo: this,
32210             cancel:false
32211         };
32212         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32213             return false;
32214         }
32215         q = qe.query;
32216         forceAll = qe.forceAll;
32217         if(forceAll === true || (q.length >= this.minChars)){
32218             if(this.lastQuery != q || this.alwaysQuery){
32219                 this.lastQuery = q;
32220                 if(this.mode == 'local'){
32221                     this.selectedIndex = -1;
32222                     if(forceAll){
32223                         this.store.clearFilter();
32224                     }else{
32225                         this.store.filter(this.displayField, q);
32226                     }
32227                     this.onLoad();
32228                 }else{
32229                     this.store.baseParams[this.queryParam] = q;
32230                     this.store.load({
32231                         params: this.getParams(q)
32232                     });
32233                     this.expand();
32234                 }
32235             }else{
32236                 this.selectedIndex = -1;
32237                 this.onLoad();   
32238             }
32239         }
32240     },
32241
32242     // private
32243     getParams : function(q){
32244         var p = {};
32245         //p[this.queryParam] = q;
32246         if(this.pageSize){
32247             p.start = 0;
32248             p.limit = this.pageSize;
32249         }
32250         return p;
32251     },
32252
32253     /**
32254      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32255      */
32256     collapse : function(){
32257         
32258     },
32259
32260     // private
32261     collapseIf : function(e){
32262         
32263     },
32264
32265     /**
32266      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32267      */
32268     expand : function(){
32269         
32270     } ,
32271
32272     // private
32273      
32274
32275     /** 
32276     * @cfg {Boolean} grow 
32277     * @hide 
32278     */
32279     /** 
32280     * @cfg {Number} growMin 
32281     * @hide 
32282     */
32283     /** 
32284     * @cfg {Number} growMax 
32285     * @hide 
32286     */
32287     /**
32288      * @hide
32289      * @method autoSize
32290      */
32291     
32292     setWidth : function()
32293     {
32294         
32295     },
32296     getResizeEl : function(){
32297         return this.el;
32298     }
32299 });//<script type="text/javasscript">
32300  
32301
32302 /**
32303  * @class Roo.DDView
32304  * A DnD enabled version of Roo.View.
32305  * @param {Element/String} container The Element in which to create the View.
32306  * @param {String} tpl The template string used to create the markup for each element of the View
32307  * @param {Object} config The configuration properties. These include all the config options of
32308  * {@link Roo.View} plus some specific to this class.<br>
32309  * <p>
32310  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32311  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32312  * <p>
32313  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32314 .x-view-drag-insert-above {
32315         border-top:1px dotted #3366cc;
32316 }
32317 .x-view-drag-insert-below {
32318         border-bottom:1px dotted #3366cc;
32319 }
32320 </code></pre>
32321  * 
32322  */
32323  
32324 Roo.DDView = function(container, tpl, config) {
32325     Roo.DDView.superclass.constructor.apply(this, arguments);
32326     this.getEl().setStyle("outline", "0px none");
32327     this.getEl().unselectable();
32328     if (this.dragGroup) {
32329                 this.setDraggable(this.dragGroup.split(","));
32330     }
32331     if (this.dropGroup) {
32332                 this.setDroppable(this.dropGroup.split(","));
32333     }
32334     if (this.deletable) {
32335         this.setDeletable();
32336     }
32337     this.isDirtyFlag = false;
32338         this.addEvents({
32339                 "drop" : true
32340         });
32341 };
32342
32343 Roo.extend(Roo.DDView, Roo.View, {
32344 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32345 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32346 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32347 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32348
32349         isFormField: true,
32350
32351         reset: Roo.emptyFn,
32352         
32353         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32354
32355         validate: function() {
32356                 return true;
32357         },
32358         
32359         destroy: function() {
32360                 this.purgeListeners();
32361                 this.getEl.removeAllListeners();
32362                 this.getEl().remove();
32363                 if (this.dragZone) {
32364                         if (this.dragZone.destroy) {
32365                                 this.dragZone.destroy();
32366                         }
32367                 }
32368                 if (this.dropZone) {
32369                         if (this.dropZone.destroy) {
32370                                 this.dropZone.destroy();
32371                         }
32372                 }
32373         },
32374
32375 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32376         getName: function() {
32377                 return this.name;
32378         },
32379
32380 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32381         setValue: function(v) {
32382                 if (!this.store) {
32383                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32384                 }
32385                 var data = {};
32386                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32387                 this.store.proxy = new Roo.data.MemoryProxy(data);
32388                 this.store.load();
32389         },
32390
32391 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32392         getValue: function() {
32393                 var result = '(';
32394                 this.store.each(function(rec) {
32395                         result += rec.id + ',';
32396                 });
32397                 return result.substr(0, result.length - 1) + ')';
32398         },
32399         
32400         getIds: function() {
32401                 var i = 0, result = new Array(this.store.getCount());
32402                 this.store.each(function(rec) {
32403                         result[i++] = rec.id;
32404                 });
32405                 return result;
32406         },
32407         
32408         isDirty: function() {
32409                 return this.isDirtyFlag;
32410         },
32411
32412 /**
32413  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32414  *      whole Element becomes the target, and this causes the drop gesture to append.
32415  */
32416     getTargetFromEvent : function(e) {
32417                 var target = e.getTarget();
32418                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32419                 target = target.parentNode;
32420                 }
32421                 if (!target) {
32422                         target = this.el.dom.lastChild || this.el.dom;
32423                 }
32424                 return target;
32425     },
32426
32427 /**
32428  *      Create the drag data which consists of an object which has the property "ddel" as
32429  *      the drag proxy element. 
32430  */
32431     getDragData : function(e) {
32432         var target = this.findItemFromChild(e.getTarget());
32433                 if(target) {
32434                         this.handleSelection(e);
32435                         var selNodes = this.getSelectedNodes();
32436             var dragData = {
32437                 source: this,
32438                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32439                 nodes: selNodes,
32440                 records: []
32441                         };
32442                         var selectedIndices = this.getSelectedIndexes();
32443                         for (var i = 0; i < selectedIndices.length; i++) {
32444                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32445                         }
32446                         if (selNodes.length == 1) {
32447                                 dragData.ddel = target.cloneNode(true); // the div element
32448                         } else {
32449                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32450                                 div.className = 'multi-proxy';
32451                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32452                                         div.appendChild(selNodes[i].cloneNode(true));
32453                                 }
32454                                 dragData.ddel = div;
32455                         }
32456             //console.log(dragData)
32457             //console.log(dragData.ddel.innerHTML)
32458                         return dragData;
32459                 }
32460         //console.log('nodragData')
32461                 return false;
32462     },
32463     
32464 /**     Specify to which ddGroup items in this DDView may be dragged. */
32465     setDraggable: function(ddGroup) {
32466         if (ddGroup instanceof Array) {
32467                 Roo.each(ddGroup, this.setDraggable, this);
32468                 return;
32469         }
32470         if (this.dragZone) {
32471                 this.dragZone.addToGroup(ddGroup);
32472         } else {
32473                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32474                                 containerScroll: true,
32475                                 ddGroup: ddGroup 
32476
32477                         });
32478 //                      Draggability implies selection. DragZone's mousedown selects the element.
32479                         if (!this.multiSelect) { this.singleSelect = true; }
32480
32481 //                      Wire the DragZone's handlers up to methods in *this*
32482                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32483                 }
32484     },
32485
32486 /**     Specify from which ddGroup this DDView accepts drops. */
32487     setDroppable: function(ddGroup) {
32488         if (ddGroup instanceof Array) {
32489                 Roo.each(ddGroup, this.setDroppable, this);
32490                 return;
32491         }
32492         if (this.dropZone) {
32493                 this.dropZone.addToGroup(ddGroup);
32494         } else {
32495                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32496                                 containerScroll: true,
32497                                 ddGroup: ddGroup
32498                         });
32499
32500 //                      Wire the DropZone's handlers up to methods in *this*
32501                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32502                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32503                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32504                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32505                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32506                 }
32507     },
32508
32509 /**     Decide whether to drop above or below a View node. */
32510     getDropPoint : function(e, n, dd){
32511         if (n == this.el.dom) { return "above"; }
32512                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32513                 var c = t + (b - t) / 2;
32514                 var y = Roo.lib.Event.getPageY(e);
32515                 if(y <= c) {
32516                         return "above";
32517                 }else{
32518                         return "below";
32519                 }
32520     },
32521
32522     onNodeEnter : function(n, dd, e, data){
32523                 return false;
32524     },
32525     
32526     onNodeOver : function(n, dd, e, data){
32527                 var pt = this.getDropPoint(e, n, dd);
32528                 // set the insert point style on the target node
32529                 var dragElClass = this.dropNotAllowed;
32530                 if (pt) {
32531                         var targetElClass;
32532                         if (pt == "above"){
32533                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32534                                 targetElClass = "x-view-drag-insert-above";
32535                         } else {
32536                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32537                                 targetElClass = "x-view-drag-insert-below";
32538                         }
32539                         if (this.lastInsertClass != targetElClass){
32540                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32541                                 this.lastInsertClass = targetElClass;
32542                         }
32543                 }
32544                 return dragElClass;
32545         },
32546
32547     onNodeOut : function(n, dd, e, data){
32548                 this.removeDropIndicators(n);
32549     },
32550
32551     onNodeDrop : function(n, dd, e, data){
32552         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32553                 return false;
32554         }
32555         var pt = this.getDropPoint(e, n, dd);
32556                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32557                 if (pt == "below") { insertAt++; }
32558                 for (var i = 0; i < data.records.length; i++) {
32559                         var r = data.records[i];
32560                         var dup = this.store.getById(r.id);
32561                         if (dup && (dd != this.dragZone)) {
32562                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32563                         } else {
32564                                 if (data.copy) {
32565                                         this.store.insert(insertAt++, r.copy());
32566                                 } else {
32567                                         data.source.isDirtyFlag = true;
32568                                         r.store.remove(r);
32569                                         this.store.insert(insertAt++, r);
32570                                 }
32571                                 this.isDirtyFlag = true;
32572                         }
32573                 }
32574                 this.dragZone.cachedTarget = null;
32575                 return true;
32576     },
32577
32578     removeDropIndicators : function(n){
32579                 if(n){
32580                         Roo.fly(n).removeClass([
32581                                 "x-view-drag-insert-above",
32582                                 "x-view-drag-insert-below"]);
32583                         this.lastInsertClass = "_noclass";
32584                 }
32585     },
32586
32587 /**
32588  *      Utility method. Add a delete option to the DDView's context menu.
32589  *      @param {String} imageUrl The URL of the "delete" icon image.
32590  */
32591         setDeletable: function(imageUrl) {
32592                 if (!this.singleSelect && !this.multiSelect) {
32593                         this.singleSelect = true;
32594                 }
32595                 var c = this.getContextMenu();
32596                 this.contextMenu.on("itemclick", function(item) {
32597                         switch (item.id) {
32598                                 case "delete":
32599                                         this.remove(this.getSelectedIndexes());
32600                                         break;
32601                         }
32602                 }, this);
32603                 this.contextMenu.add({
32604                         icon: imageUrl,
32605                         id: "delete",
32606                         text: 'Delete'
32607                 });
32608         },
32609         
32610 /**     Return the context menu for this DDView. */
32611         getContextMenu: function() {
32612                 if (!this.contextMenu) {
32613 //                      Create the View's context menu
32614                         this.contextMenu = new Roo.menu.Menu({
32615                                 id: this.id + "-contextmenu"
32616                         });
32617                         this.el.on("contextmenu", this.showContextMenu, this);
32618                 }
32619                 return this.contextMenu;
32620         },
32621         
32622         disableContextMenu: function() {
32623                 if (this.contextMenu) {
32624                         this.el.un("contextmenu", this.showContextMenu, this);
32625                 }
32626         },
32627
32628         showContextMenu: function(e, item) {
32629         item = this.findItemFromChild(e.getTarget());
32630                 if (item) {
32631                         e.stopEvent();
32632                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32633                         this.contextMenu.showAt(e.getXY());
32634             }
32635     },
32636
32637 /**
32638  *      Remove {@link Roo.data.Record}s at the specified indices.
32639  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32640  */
32641     remove: function(selectedIndices) {
32642                 selectedIndices = [].concat(selectedIndices);
32643                 for (var i = 0; i < selectedIndices.length; i++) {
32644                         var rec = this.store.getAt(selectedIndices[i]);
32645                         this.store.remove(rec);
32646                 }
32647     },
32648
32649 /**
32650  *      Double click fires the event, but also, if this is draggable, and there is only one other
32651  *      related DropZone, it transfers the selected node.
32652  */
32653     onDblClick : function(e){
32654         var item = this.findItemFromChild(e.getTarget());
32655         if(item){
32656             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32657                 return false;
32658             }
32659             if (this.dragGroup) {
32660                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32661                     while (targets.indexOf(this.dropZone) > -1) {
32662                             targets.remove(this.dropZone);
32663                                 }
32664                     if (targets.length == 1) {
32665                                         this.dragZone.cachedTarget = null;
32666                         var el = Roo.get(targets[0].getEl());
32667                         var box = el.getBox(true);
32668                         targets[0].onNodeDrop(el.dom, {
32669                                 target: el.dom,
32670                                 xy: [box.x, box.y + box.height - 1]
32671                         }, null, this.getDragData(e));
32672                     }
32673                 }
32674         }
32675     },
32676     
32677     handleSelection: function(e) {
32678                 this.dragZone.cachedTarget = null;
32679         var item = this.findItemFromChild(e.getTarget());
32680         if (!item) {
32681                 this.clearSelections(true);
32682                 return;
32683         }
32684                 if (item && (this.multiSelect || this.singleSelect)){
32685                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32686                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32687                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32688                                 this.unselect(item);
32689                         } else {
32690                                 this.select(item, this.multiSelect && e.ctrlKey);
32691                                 this.lastSelection = item;
32692                         }
32693                 }
32694     },
32695
32696     onItemClick : function(item, index, e){
32697                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32698                         return false;
32699                 }
32700                 return true;
32701     },
32702
32703     unselect : function(nodeInfo, suppressEvent){
32704                 var node = this.getNode(nodeInfo);
32705                 if(node && this.isSelected(node)){
32706                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32707                                 Roo.fly(node).removeClass(this.selectedClass);
32708                                 this.selections.remove(node);
32709                                 if(!suppressEvent){
32710                                         this.fireEvent("selectionchange", this, this.selections);
32711                                 }
32712                         }
32713                 }
32714     }
32715 });
32716 /*
32717  * Based on:
32718  * Ext JS Library 1.1.1
32719  * Copyright(c) 2006-2007, Ext JS, LLC.
32720  *
32721  * Originally Released Under LGPL - original licence link has changed is not relivant.
32722  *
32723  * Fork - LGPL
32724  * <script type="text/javascript">
32725  */
32726  
32727 /**
32728  * @class Roo.LayoutManager
32729  * @extends Roo.util.Observable
32730  * Base class for layout managers.
32731  */
32732 Roo.LayoutManager = function(container, config){
32733     Roo.LayoutManager.superclass.constructor.call(this);
32734     this.el = Roo.get(container);
32735     // ie scrollbar fix
32736     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32737         document.body.scroll = "no";
32738     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32739         this.el.position('relative');
32740     }
32741     this.id = this.el.id;
32742     this.el.addClass("x-layout-container");
32743     /** false to disable window resize monitoring @type Boolean */
32744     this.monitorWindowResize = true;
32745     this.regions = {};
32746     this.addEvents({
32747         /**
32748          * @event layout
32749          * Fires when a layout is performed. 
32750          * @param {Roo.LayoutManager} this
32751          */
32752         "layout" : true,
32753         /**
32754          * @event regionresized
32755          * Fires when the user resizes a region. 
32756          * @param {Roo.LayoutRegion} region The resized region
32757          * @param {Number} newSize The new size (width for east/west, height for north/south)
32758          */
32759         "regionresized" : true,
32760         /**
32761          * @event regioncollapsed
32762          * Fires when a region is collapsed. 
32763          * @param {Roo.LayoutRegion} region The collapsed region
32764          */
32765         "regioncollapsed" : true,
32766         /**
32767          * @event regionexpanded
32768          * Fires when a region is expanded.  
32769          * @param {Roo.LayoutRegion} region The expanded region
32770          */
32771         "regionexpanded" : true
32772     });
32773     this.updating = false;
32774     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32775 };
32776
32777 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32778     /**
32779      * Returns true if this layout is currently being updated
32780      * @return {Boolean}
32781      */
32782     isUpdating : function(){
32783         return this.updating; 
32784     },
32785     
32786     /**
32787      * Suspend the LayoutManager from doing auto-layouts while
32788      * making multiple add or remove calls
32789      */
32790     beginUpdate : function(){
32791         this.updating = true;    
32792     },
32793     
32794     /**
32795      * Restore auto-layouts and optionally disable the manager from performing a layout
32796      * @param {Boolean} noLayout true to disable a layout update 
32797      */
32798     endUpdate : function(noLayout){
32799         this.updating = false;
32800         if(!noLayout){
32801             this.layout();
32802         }    
32803     },
32804     
32805     layout: function(){
32806         
32807     },
32808     
32809     onRegionResized : function(region, newSize){
32810         this.fireEvent("regionresized", region, newSize);
32811         this.layout();
32812     },
32813     
32814     onRegionCollapsed : function(region){
32815         this.fireEvent("regioncollapsed", region);
32816     },
32817     
32818     onRegionExpanded : function(region){
32819         this.fireEvent("regionexpanded", region);
32820     },
32821         
32822     /**
32823      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32824      * performs box-model adjustments.
32825      * @return {Object} The size as an object {width: (the width), height: (the height)}
32826      */
32827     getViewSize : function(){
32828         var size;
32829         if(this.el.dom != document.body){
32830             size = this.el.getSize();
32831         }else{
32832             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32833         }
32834         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32835         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32836         return size;
32837     },
32838     
32839     /**
32840      * Returns the Element this layout is bound to.
32841      * @return {Roo.Element}
32842      */
32843     getEl : function(){
32844         return this.el;
32845     },
32846     
32847     /**
32848      * Returns the specified region.
32849      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32850      * @return {Roo.LayoutRegion}
32851      */
32852     getRegion : function(target){
32853         return this.regions[target.toLowerCase()];
32854     },
32855     
32856     onWindowResize : function(){
32857         if(this.monitorWindowResize){
32858             this.layout();
32859         }
32860     }
32861 });/*
32862  * Based on:
32863  * Ext JS Library 1.1.1
32864  * Copyright(c) 2006-2007, Ext JS, LLC.
32865  *
32866  * Originally Released Under LGPL - original licence link has changed is not relivant.
32867  *
32868  * Fork - LGPL
32869  * <script type="text/javascript">
32870  */
32871 /**
32872  * @class Roo.BorderLayout
32873  * @extends Roo.LayoutManager
32874  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32875  * please see: <br><br>
32876  * <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>
32877  * <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>
32878  * Example:
32879  <pre><code>
32880  var layout = new Roo.BorderLayout(document.body, {
32881     north: {
32882         initialSize: 25,
32883         titlebar: false
32884     },
32885     west: {
32886         split:true,
32887         initialSize: 200,
32888         minSize: 175,
32889         maxSize: 400,
32890         titlebar: true,
32891         collapsible: true
32892     },
32893     east: {
32894         split:true,
32895         initialSize: 202,
32896         minSize: 175,
32897         maxSize: 400,
32898         titlebar: true,
32899         collapsible: true
32900     },
32901     south: {
32902         split:true,
32903         initialSize: 100,
32904         minSize: 100,
32905         maxSize: 200,
32906         titlebar: true,
32907         collapsible: true
32908     },
32909     center: {
32910         titlebar: true,
32911         autoScroll:true,
32912         resizeTabs: true,
32913         minTabWidth: 50,
32914         preferredTabWidth: 150
32915     }
32916 });
32917
32918 // shorthand
32919 var CP = Roo.ContentPanel;
32920
32921 layout.beginUpdate();
32922 layout.add("north", new CP("north", "North"));
32923 layout.add("south", new CP("south", {title: "South", closable: true}));
32924 layout.add("west", new CP("west", {title: "West"}));
32925 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32926 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32927 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32928 layout.getRegion("center").showPanel("center1");
32929 layout.endUpdate();
32930 </code></pre>
32931
32932 <b>The container the layout is rendered into can be either the body element or any other element.
32933 If it is not the body element, the container needs to either be an absolute positioned element,
32934 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32935 the container size if it is not the body element.</b>
32936
32937 * @constructor
32938 * Create a new BorderLayout
32939 * @param {String/HTMLElement/Element} container The container this layout is bound to
32940 * @param {Object} config Configuration options
32941  */
32942 Roo.BorderLayout = function(container, config){
32943     config = config || {};
32944     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32945     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32946     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32947         var target = this.factory.validRegions[i];
32948         if(config[target]){
32949             this.addRegion(target, config[target]);
32950         }
32951     }
32952 };
32953
32954 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32955     /**
32956      * Creates and adds a new region if it doesn't already exist.
32957      * @param {String} target The target region key (north, south, east, west or center).
32958      * @param {Object} config The regions config object
32959      * @return {BorderLayoutRegion} The new region
32960      */
32961     addRegion : function(target, config){
32962         if(!this.regions[target]){
32963             var r = this.factory.create(target, this, config);
32964             this.bindRegion(target, r);
32965         }
32966         return this.regions[target];
32967     },
32968
32969     // private (kinda)
32970     bindRegion : function(name, r){
32971         this.regions[name] = r;
32972         r.on("visibilitychange", this.layout, this);
32973         r.on("paneladded", this.layout, this);
32974         r.on("panelremoved", this.layout, this);
32975         r.on("invalidated", this.layout, this);
32976         r.on("resized", this.onRegionResized, this);
32977         r.on("collapsed", this.onRegionCollapsed, this);
32978         r.on("expanded", this.onRegionExpanded, this);
32979     },
32980
32981     /**
32982      * Performs a layout update.
32983      */
32984     layout : function(){
32985         if(this.updating) {
32986             return;
32987         }
32988         var size = this.getViewSize();
32989         var w = size.width;
32990         var h = size.height;
32991         var centerW = w;
32992         var centerH = h;
32993         var centerY = 0;
32994         var centerX = 0;
32995         //var x = 0, y = 0;
32996
32997         var rs = this.regions;
32998         var north = rs["north"];
32999         var south = rs["south"]; 
33000         var west = rs["west"];
33001         var east = rs["east"];
33002         var center = rs["center"];
33003         //if(this.hideOnLayout){ // not supported anymore
33004             //c.el.setStyle("display", "none");
33005         //}
33006         if(north && north.isVisible()){
33007             var b = north.getBox();
33008             var m = north.getMargins();
33009             b.width = w - (m.left+m.right);
33010             b.x = m.left;
33011             b.y = m.top;
33012             centerY = b.height + b.y + m.bottom;
33013             centerH -= centerY;
33014             north.updateBox(this.safeBox(b));
33015         }
33016         if(south && south.isVisible()){
33017             var b = south.getBox();
33018             var m = south.getMargins();
33019             b.width = w - (m.left+m.right);
33020             b.x = m.left;
33021             var totalHeight = (b.height + m.top + m.bottom);
33022             b.y = h - totalHeight + m.top;
33023             centerH -= totalHeight;
33024             south.updateBox(this.safeBox(b));
33025         }
33026         if(west && west.isVisible()){
33027             var b = west.getBox();
33028             var m = west.getMargins();
33029             b.height = centerH - (m.top+m.bottom);
33030             b.x = m.left;
33031             b.y = centerY + m.top;
33032             var totalWidth = (b.width + m.left + m.right);
33033             centerX += totalWidth;
33034             centerW -= totalWidth;
33035             west.updateBox(this.safeBox(b));
33036         }
33037         if(east && east.isVisible()){
33038             var b = east.getBox();
33039             var m = east.getMargins();
33040             b.height = centerH - (m.top+m.bottom);
33041             var totalWidth = (b.width + m.left + m.right);
33042             b.x = w - totalWidth + m.left;
33043             b.y = centerY + m.top;
33044             centerW -= totalWidth;
33045             east.updateBox(this.safeBox(b));
33046         }
33047         if(center){
33048             var m = center.getMargins();
33049             var centerBox = {
33050                 x: centerX + m.left,
33051                 y: centerY + m.top,
33052                 width: centerW - (m.left+m.right),
33053                 height: centerH - (m.top+m.bottom)
33054             };
33055             //if(this.hideOnLayout){
33056                 //center.el.setStyle("display", "block");
33057             //}
33058             center.updateBox(this.safeBox(centerBox));
33059         }
33060         this.el.repaint();
33061         this.fireEvent("layout", this);
33062     },
33063
33064     // private
33065     safeBox : function(box){
33066         box.width = Math.max(0, box.width);
33067         box.height = Math.max(0, box.height);
33068         return box;
33069     },
33070
33071     /**
33072      * Adds a ContentPanel (or subclass) to this layout.
33073      * @param {String} target The target region key (north, south, east, west or center).
33074      * @param {Roo.ContentPanel} panel The panel to add
33075      * @return {Roo.ContentPanel} The added panel
33076      */
33077     add : function(target, panel){
33078          
33079         target = target.toLowerCase();
33080         return this.regions[target].add(panel);
33081     },
33082
33083     /**
33084      * Remove a ContentPanel (or subclass) to this layout.
33085      * @param {String} target The target region key (north, south, east, west or center).
33086      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33087      * @return {Roo.ContentPanel} The removed panel
33088      */
33089     remove : function(target, panel){
33090         target = target.toLowerCase();
33091         return this.regions[target].remove(panel);
33092     },
33093
33094     /**
33095      * Searches all regions for a panel with the specified id
33096      * @param {String} panelId
33097      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33098      */
33099     findPanel : function(panelId){
33100         var rs = this.regions;
33101         for(var target in rs){
33102             if(typeof rs[target] != "function"){
33103                 var p = rs[target].getPanel(panelId);
33104                 if(p){
33105                     return p;
33106                 }
33107             }
33108         }
33109         return null;
33110     },
33111
33112     /**
33113      * Searches all regions for a panel with the specified id and activates (shows) it.
33114      * @param {String/ContentPanel} panelId The panels id or the panel itself
33115      * @return {Roo.ContentPanel} The shown panel or null
33116      */
33117     showPanel : function(panelId) {
33118       var rs = this.regions;
33119       for(var target in rs){
33120          var r = rs[target];
33121          if(typeof r != "function"){
33122             if(r.hasPanel(panelId)){
33123                return r.showPanel(panelId);
33124             }
33125          }
33126       }
33127       return null;
33128    },
33129
33130    /**
33131      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33132      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33133      */
33134     restoreState : function(provider){
33135         if(!provider){
33136             provider = Roo.state.Manager;
33137         }
33138         var sm = new Roo.LayoutStateManager();
33139         sm.init(this, provider);
33140     },
33141
33142     /**
33143      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33144      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33145      * a valid ContentPanel config object.  Example:
33146      * <pre><code>
33147 // Create the main layout
33148 var layout = new Roo.BorderLayout('main-ct', {
33149     west: {
33150         split:true,
33151         minSize: 175,
33152         titlebar: true
33153     },
33154     center: {
33155         title:'Components'
33156     }
33157 }, 'main-ct');
33158
33159 // Create and add multiple ContentPanels at once via configs
33160 layout.batchAdd({
33161    west: {
33162        id: 'source-files',
33163        autoCreate:true,
33164        title:'Ext Source Files',
33165        autoScroll:true,
33166        fitToFrame:true
33167    },
33168    center : {
33169        el: cview,
33170        autoScroll:true,
33171        fitToFrame:true,
33172        toolbar: tb,
33173        resizeEl:'cbody'
33174    }
33175 });
33176 </code></pre>
33177      * @param {Object} regions An object containing ContentPanel configs by region name
33178      */
33179     batchAdd : function(regions){
33180         this.beginUpdate();
33181         for(var rname in regions){
33182             var lr = this.regions[rname];
33183             if(lr){
33184                 this.addTypedPanels(lr, regions[rname]);
33185             }
33186         }
33187         this.endUpdate();
33188     },
33189
33190     // private
33191     addTypedPanels : function(lr, ps){
33192         if(typeof ps == 'string'){
33193             lr.add(new Roo.ContentPanel(ps));
33194         }
33195         else if(ps instanceof Array){
33196             for(var i =0, len = ps.length; i < len; i++){
33197                 this.addTypedPanels(lr, ps[i]);
33198             }
33199         }
33200         else if(!ps.events){ // raw config?
33201             var el = ps.el;
33202             delete ps.el; // prevent conflict
33203             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33204         }
33205         else {  // panel object assumed!
33206             lr.add(ps);
33207         }
33208     },
33209     /**
33210      * Adds a xtype elements to the layout.
33211      * <pre><code>
33212
33213 layout.addxtype({
33214        xtype : 'ContentPanel',
33215        region: 'west',
33216        items: [ .... ]
33217    }
33218 );
33219
33220 layout.addxtype({
33221         xtype : 'NestedLayoutPanel',
33222         region: 'west',
33223         layout: {
33224            center: { },
33225            west: { }   
33226         },
33227         items : [ ... list of content panels or nested layout panels.. ]
33228    }
33229 );
33230 </code></pre>
33231      * @param {Object} cfg Xtype definition of item to add.
33232      */
33233     addxtype : function(cfg)
33234     {
33235         // basically accepts a pannel...
33236         // can accept a layout region..!?!?
33237         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33238         
33239         if (!cfg.xtype.match(/Panel$/)) {
33240             return false;
33241         }
33242         var ret = false;
33243         
33244         if (typeof(cfg.region) == 'undefined') {
33245             Roo.log("Failed to add Panel, region was not set");
33246             Roo.log(cfg);
33247             return false;
33248         }
33249         var region = cfg.region;
33250         delete cfg.region;
33251         
33252           
33253         var xitems = [];
33254         if (cfg.items) {
33255             xitems = cfg.items;
33256             delete cfg.items;
33257         }
33258         var nb = false;
33259         
33260         switch(cfg.xtype) 
33261         {
33262             case 'ContentPanel':  // ContentPanel (el, cfg)
33263             case 'ScrollPanel':  // ContentPanel (el, cfg)
33264             case 'ViewPanel': 
33265                 if(cfg.autoCreate) {
33266                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33267                 } else {
33268                     var el = this.el.createChild();
33269                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33270                 }
33271                 
33272                 this.add(region, ret);
33273                 break;
33274             
33275             
33276             case 'TreePanel': // our new panel!
33277                 cfg.el = this.el.createChild();
33278                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33279                 this.add(region, ret);
33280                 break;
33281             
33282             case 'NestedLayoutPanel': 
33283                 // create a new Layout (which is  a Border Layout...
33284                 var el = this.el.createChild();
33285                 var clayout = cfg.layout;
33286                 delete cfg.layout;
33287                 clayout.items   = clayout.items  || [];
33288                 // replace this exitems with the clayout ones..
33289                 xitems = clayout.items;
33290                  
33291                 
33292                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33293                     cfg.background = false;
33294                 }
33295                 var layout = new Roo.BorderLayout(el, clayout);
33296                 
33297                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33298                 //console.log('adding nested layout panel '  + cfg.toSource());
33299                 this.add(region, ret);
33300                 nb = {}; /// find first...
33301                 break;
33302                 
33303             case 'GridPanel': 
33304             
33305                 // needs grid and region
33306                 
33307                 //var el = this.getRegion(region).el.createChild();
33308                 var el = this.el.createChild();
33309                 // create the grid first...
33310                 
33311                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33312                 delete cfg.grid;
33313                 if (region == 'center' && this.active ) {
33314                     cfg.background = false;
33315                 }
33316                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33317                 
33318                 this.add(region, ret);
33319                 if (cfg.background) {
33320                     ret.on('activate', function(gp) {
33321                         if (!gp.grid.rendered) {
33322                             gp.grid.render();
33323                         }
33324                     });
33325                 } else {
33326                     grid.render();
33327                 }
33328                 break;
33329            
33330            
33331            
33332                 
33333                 
33334                 
33335             default:
33336                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33337                     
33338                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33339                     this.add(region, ret);
33340                 } else {
33341                 
33342                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33343                     return null;
33344                 }
33345                 
33346              // GridPanel (grid, cfg)
33347             
33348         }
33349         this.beginUpdate();
33350         // add children..
33351         var region = '';
33352         var abn = {};
33353         Roo.each(xitems, function(i)  {
33354             region = nb && i.region ? i.region : false;
33355             
33356             var add = ret.addxtype(i);
33357            
33358             if (region) {
33359                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33360                 if (!i.background) {
33361                     abn[region] = nb[region] ;
33362                 }
33363             }
33364             
33365         });
33366         this.endUpdate();
33367
33368         // make the last non-background panel active..
33369         //if (nb) { Roo.log(abn); }
33370         if (nb) {
33371             
33372             for(var r in abn) {
33373                 region = this.getRegion(r);
33374                 if (region) {
33375                     // tried using nb[r], but it does not work..
33376                      
33377                     region.showPanel(abn[r]);
33378                    
33379                 }
33380             }
33381         }
33382         return ret;
33383         
33384     }
33385 });
33386
33387 /**
33388  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33389  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33390  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33391  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33392  * <pre><code>
33393 // shorthand
33394 var CP = Roo.ContentPanel;
33395
33396 var layout = Roo.BorderLayout.create({
33397     north: {
33398         initialSize: 25,
33399         titlebar: false,
33400         panels: [new CP("north", "North")]
33401     },
33402     west: {
33403         split:true,
33404         initialSize: 200,
33405         minSize: 175,
33406         maxSize: 400,
33407         titlebar: true,
33408         collapsible: true,
33409         panels: [new CP("west", {title: "West"})]
33410     },
33411     east: {
33412         split:true,
33413         initialSize: 202,
33414         minSize: 175,
33415         maxSize: 400,
33416         titlebar: true,
33417         collapsible: true,
33418         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33419     },
33420     south: {
33421         split:true,
33422         initialSize: 100,
33423         minSize: 100,
33424         maxSize: 200,
33425         titlebar: true,
33426         collapsible: true,
33427         panels: [new CP("south", {title: "South", closable: true})]
33428     },
33429     center: {
33430         titlebar: true,
33431         autoScroll:true,
33432         resizeTabs: true,
33433         minTabWidth: 50,
33434         preferredTabWidth: 150,
33435         panels: [
33436             new CP("center1", {title: "Close Me", closable: true}),
33437             new CP("center2", {title: "Center Panel", closable: false})
33438         ]
33439     }
33440 }, document.body);
33441
33442 layout.getRegion("center").showPanel("center1");
33443 </code></pre>
33444  * @param config
33445  * @param targetEl
33446  */
33447 Roo.BorderLayout.create = function(config, targetEl){
33448     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33449     layout.beginUpdate();
33450     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33451     for(var j = 0, jlen = regions.length; j < jlen; j++){
33452         var lr = regions[j];
33453         if(layout.regions[lr] && config[lr].panels){
33454             var r = layout.regions[lr];
33455             var ps = config[lr].panels;
33456             layout.addTypedPanels(r, ps);
33457         }
33458     }
33459     layout.endUpdate();
33460     return layout;
33461 };
33462
33463 // private
33464 Roo.BorderLayout.RegionFactory = {
33465     // private
33466     validRegions : ["north","south","east","west","center"],
33467
33468     // private
33469     create : function(target, mgr, config){
33470         target = target.toLowerCase();
33471         if(config.lightweight || config.basic){
33472             return new Roo.BasicLayoutRegion(mgr, config, target);
33473         }
33474         switch(target){
33475             case "north":
33476                 return new Roo.NorthLayoutRegion(mgr, config);
33477             case "south":
33478                 return new Roo.SouthLayoutRegion(mgr, config);
33479             case "east":
33480                 return new Roo.EastLayoutRegion(mgr, config);
33481             case "west":
33482                 return new Roo.WestLayoutRegion(mgr, config);
33483             case "center":
33484                 return new Roo.CenterLayoutRegion(mgr, config);
33485         }
33486         throw 'Layout region "'+target+'" not supported.';
33487     }
33488 };/*
33489  * Based on:
33490  * Ext JS Library 1.1.1
33491  * Copyright(c) 2006-2007, Ext JS, LLC.
33492  *
33493  * Originally Released Under LGPL - original licence link has changed is not relivant.
33494  *
33495  * Fork - LGPL
33496  * <script type="text/javascript">
33497  */
33498  
33499 /**
33500  * @class Roo.BasicLayoutRegion
33501  * @extends Roo.util.Observable
33502  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33503  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33504  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33505  */
33506 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33507     this.mgr = mgr;
33508     this.position  = pos;
33509     this.events = {
33510         /**
33511          * @scope Roo.BasicLayoutRegion
33512          */
33513         
33514         /**
33515          * @event beforeremove
33516          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33517          * @param {Roo.LayoutRegion} this
33518          * @param {Roo.ContentPanel} panel The panel
33519          * @param {Object} e The cancel event object
33520          */
33521         "beforeremove" : true,
33522         /**
33523          * @event invalidated
33524          * Fires when the layout for this region is changed.
33525          * @param {Roo.LayoutRegion} this
33526          */
33527         "invalidated" : true,
33528         /**
33529          * @event visibilitychange
33530          * Fires when this region is shown or hidden 
33531          * @param {Roo.LayoutRegion} this
33532          * @param {Boolean} visibility true or false
33533          */
33534         "visibilitychange" : true,
33535         /**
33536          * @event paneladded
33537          * Fires when a panel is added. 
33538          * @param {Roo.LayoutRegion} this
33539          * @param {Roo.ContentPanel} panel The panel
33540          */
33541         "paneladded" : true,
33542         /**
33543          * @event panelremoved
33544          * Fires when a panel is removed. 
33545          * @param {Roo.LayoutRegion} this
33546          * @param {Roo.ContentPanel} panel The panel
33547          */
33548         "panelremoved" : true,
33549         /**
33550          * @event collapsed
33551          * Fires when this region is collapsed.
33552          * @param {Roo.LayoutRegion} this
33553          */
33554         "collapsed" : true,
33555         /**
33556          * @event expanded
33557          * Fires when this region is expanded.
33558          * @param {Roo.LayoutRegion} this
33559          */
33560         "expanded" : true,
33561         /**
33562          * @event slideshow
33563          * Fires when this region is slid into view.
33564          * @param {Roo.LayoutRegion} this
33565          */
33566         "slideshow" : true,
33567         /**
33568          * @event slidehide
33569          * Fires when this region slides out of view. 
33570          * @param {Roo.LayoutRegion} this
33571          */
33572         "slidehide" : true,
33573         /**
33574          * @event panelactivated
33575          * Fires when a panel is activated. 
33576          * @param {Roo.LayoutRegion} this
33577          * @param {Roo.ContentPanel} panel The activated panel
33578          */
33579         "panelactivated" : true,
33580         /**
33581          * @event resized
33582          * Fires when the user resizes this region. 
33583          * @param {Roo.LayoutRegion} this
33584          * @param {Number} newSize The new size (width for east/west, height for north/south)
33585          */
33586         "resized" : true
33587     };
33588     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33589     this.panels = new Roo.util.MixedCollection();
33590     this.panels.getKey = this.getPanelId.createDelegate(this);
33591     this.box = null;
33592     this.activePanel = null;
33593     // ensure listeners are added...
33594     
33595     if (config.listeners || config.events) {
33596         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33597             listeners : config.listeners || {},
33598             events : config.events || {}
33599         });
33600     }
33601     
33602     if(skipConfig !== true){
33603         this.applyConfig(config);
33604     }
33605 };
33606
33607 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33608     getPanelId : function(p){
33609         return p.getId();
33610     },
33611     
33612     applyConfig : function(config){
33613         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33614         this.config = config;
33615         
33616     },
33617     
33618     /**
33619      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33620      * the width, for horizontal (north, south) the height.
33621      * @param {Number} newSize The new width or height
33622      */
33623     resizeTo : function(newSize){
33624         var el = this.el ? this.el :
33625                  (this.activePanel ? this.activePanel.getEl() : null);
33626         if(el){
33627             switch(this.position){
33628                 case "east":
33629                 case "west":
33630                     el.setWidth(newSize);
33631                     this.fireEvent("resized", this, newSize);
33632                 break;
33633                 case "north":
33634                 case "south":
33635                     el.setHeight(newSize);
33636                     this.fireEvent("resized", this, newSize);
33637                 break;                
33638             }
33639         }
33640     },
33641     
33642     getBox : function(){
33643         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33644     },
33645     
33646     getMargins : function(){
33647         return this.margins;
33648     },
33649     
33650     updateBox : function(box){
33651         this.box = box;
33652         var el = this.activePanel.getEl();
33653         el.dom.style.left = box.x + "px";
33654         el.dom.style.top = box.y + "px";
33655         this.activePanel.setSize(box.width, box.height);
33656     },
33657     
33658     /**
33659      * Returns the container element for this region.
33660      * @return {Roo.Element}
33661      */
33662     getEl : function(){
33663         return this.activePanel;
33664     },
33665     
33666     /**
33667      * Returns true if this region is currently visible.
33668      * @return {Boolean}
33669      */
33670     isVisible : function(){
33671         return this.activePanel ? true : false;
33672     },
33673     
33674     setActivePanel : function(panel){
33675         panel = this.getPanel(panel);
33676         if(this.activePanel && this.activePanel != panel){
33677             this.activePanel.setActiveState(false);
33678             this.activePanel.getEl().setLeftTop(-10000,-10000);
33679         }
33680         this.activePanel = panel;
33681         panel.setActiveState(true);
33682         if(this.box){
33683             panel.setSize(this.box.width, this.box.height);
33684         }
33685         this.fireEvent("panelactivated", this, panel);
33686         this.fireEvent("invalidated");
33687     },
33688     
33689     /**
33690      * Show the specified panel.
33691      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33692      * @return {Roo.ContentPanel} The shown panel or null
33693      */
33694     showPanel : function(panel){
33695         if(panel = this.getPanel(panel)){
33696             this.setActivePanel(panel);
33697         }
33698         return panel;
33699     },
33700     
33701     /**
33702      * Get the active panel for this region.
33703      * @return {Roo.ContentPanel} The active panel or null
33704      */
33705     getActivePanel : function(){
33706         return this.activePanel;
33707     },
33708     
33709     /**
33710      * Add the passed ContentPanel(s)
33711      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33712      * @return {Roo.ContentPanel} The panel added (if only one was added)
33713      */
33714     add : function(panel){
33715         if(arguments.length > 1){
33716             for(var i = 0, len = arguments.length; i < len; i++) {
33717                 this.add(arguments[i]);
33718             }
33719             return null;
33720         }
33721         if(this.hasPanel(panel)){
33722             this.showPanel(panel);
33723             return panel;
33724         }
33725         var el = panel.getEl();
33726         if(el.dom.parentNode != this.mgr.el.dom){
33727             this.mgr.el.dom.appendChild(el.dom);
33728         }
33729         if(panel.setRegion){
33730             panel.setRegion(this);
33731         }
33732         this.panels.add(panel);
33733         el.setStyle("position", "absolute");
33734         if(!panel.background){
33735             this.setActivePanel(panel);
33736             if(this.config.initialSize && this.panels.getCount()==1){
33737                 this.resizeTo(this.config.initialSize);
33738             }
33739         }
33740         this.fireEvent("paneladded", this, panel);
33741         return panel;
33742     },
33743     
33744     /**
33745      * Returns true if the panel is in this region.
33746      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33747      * @return {Boolean}
33748      */
33749     hasPanel : function(panel){
33750         if(typeof panel == "object"){ // must be panel obj
33751             panel = panel.getId();
33752         }
33753         return this.getPanel(panel) ? true : false;
33754     },
33755     
33756     /**
33757      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33758      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33759      * @param {Boolean} preservePanel Overrides the config preservePanel option
33760      * @return {Roo.ContentPanel} The panel that was removed
33761      */
33762     remove : function(panel, preservePanel){
33763         panel = this.getPanel(panel);
33764         if(!panel){
33765             return null;
33766         }
33767         var e = {};
33768         this.fireEvent("beforeremove", this, panel, e);
33769         if(e.cancel === true){
33770             return null;
33771         }
33772         var panelId = panel.getId();
33773         this.panels.removeKey(panelId);
33774         return panel;
33775     },
33776     
33777     /**
33778      * Returns the panel specified or null if it's not in this region.
33779      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33780      * @return {Roo.ContentPanel}
33781      */
33782     getPanel : function(id){
33783         if(typeof id == "object"){ // must be panel obj
33784             return id;
33785         }
33786         return this.panels.get(id);
33787     },
33788     
33789     /**
33790      * Returns this regions position (north/south/east/west/center).
33791      * @return {String} 
33792      */
33793     getPosition: function(){
33794         return this.position;    
33795     }
33796 });/*
33797  * Based on:
33798  * Ext JS Library 1.1.1
33799  * Copyright(c) 2006-2007, Ext JS, LLC.
33800  *
33801  * Originally Released Under LGPL - original licence link has changed is not relivant.
33802  *
33803  * Fork - LGPL
33804  * <script type="text/javascript">
33805  */
33806  
33807 /**
33808  * @class Roo.LayoutRegion
33809  * @extends Roo.BasicLayoutRegion
33810  * This class represents a region in a layout manager.
33811  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33812  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33813  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33814  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33815  * @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})
33816  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33817  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33818  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33819  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33820  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33821  * @cfg {String}    title           The title for the region (overrides panel titles)
33822  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33823  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33824  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33825  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33826  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33827  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33828  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33829  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33830  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33831  * @cfg {Boolean}   showPin         True to show a pin button
33832  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33833  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33834  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33835  * @cfg {Number}    width           For East/West panels
33836  * @cfg {Number}    height          For North/South panels
33837  * @cfg {Boolean}   split           To show the splitter
33838  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33839  */
33840 Roo.LayoutRegion = function(mgr, config, pos){
33841     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33842     var dh = Roo.DomHelper;
33843     /** This region's container element 
33844     * @type Roo.Element */
33845     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33846     /** This region's title element 
33847     * @type Roo.Element */
33848
33849     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33850         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33851         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33852     ]}, true);
33853     this.titleEl.enableDisplayMode();
33854     /** This region's title text element 
33855     * @type HTMLElement */
33856     this.titleTextEl = this.titleEl.dom.firstChild;
33857     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33858     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33859     this.closeBtn.enableDisplayMode();
33860     this.closeBtn.on("click", this.closeClicked, this);
33861     this.closeBtn.hide();
33862
33863     this.createBody(config);
33864     this.visible = true;
33865     this.collapsed = false;
33866
33867     if(config.hideWhenEmpty){
33868         this.hide();
33869         this.on("paneladded", this.validateVisibility, this);
33870         this.on("panelremoved", this.validateVisibility, this);
33871     }
33872     this.applyConfig(config);
33873 };
33874
33875 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33876
33877     createBody : function(){
33878         /** This region's body element 
33879         * @type Roo.Element */
33880         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33881     },
33882
33883     applyConfig : function(c){
33884         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33885             var dh = Roo.DomHelper;
33886             if(c.titlebar !== false){
33887                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33888                 this.collapseBtn.on("click", this.collapse, this);
33889                 this.collapseBtn.enableDisplayMode();
33890
33891                 if(c.showPin === true || this.showPin){
33892                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33893                     this.stickBtn.enableDisplayMode();
33894                     this.stickBtn.on("click", this.expand, this);
33895                     this.stickBtn.hide();
33896                 }
33897             }
33898             /** This region's collapsed element
33899             * @type Roo.Element */
33900             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33901                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33902             ]}, true);
33903             if(c.floatable !== false){
33904                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33905                this.collapsedEl.on("click", this.collapseClick, this);
33906             }
33907
33908             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33909                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33910                    id: "message", unselectable: "on", style:{"float":"left"}});
33911                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33912              }
33913             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33914             this.expandBtn.on("click", this.expand, this);
33915         }
33916         if(this.collapseBtn){
33917             this.collapseBtn.setVisible(c.collapsible == true);
33918         }
33919         this.cmargins = c.cmargins || this.cmargins ||
33920                          (this.position == "west" || this.position == "east" ?
33921                              {top: 0, left: 2, right:2, bottom: 0} :
33922                              {top: 2, left: 0, right:0, bottom: 2});
33923         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33924         this.bottomTabs = c.tabPosition != "top";
33925         this.autoScroll = c.autoScroll || false;
33926         if(this.autoScroll){
33927             this.bodyEl.setStyle("overflow", "auto");
33928         }else{
33929             this.bodyEl.setStyle("overflow", "hidden");
33930         }
33931         //if(c.titlebar !== false){
33932             if((!c.titlebar && !c.title) || c.titlebar === false){
33933                 this.titleEl.hide();
33934             }else{
33935                 this.titleEl.show();
33936                 if(c.title){
33937                     this.titleTextEl.innerHTML = c.title;
33938                 }
33939             }
33940         //}
33941         this.duration = c.duration || .30;
33942         this.slideDuration = c.slideDuration || .45;
33943         this.config = c;
33944         if(c.collapsed){
33945             this.collapse(true);
33946         }
33947         if(c.hidden){
33948             this.hide();
33949         }
33950     },
33951     /**
33952      * Returns true if this region is currently visible.
33953      * @return {Boolean}
33954      */
33955     isVisible : function(){
33956         return this.visible;
33957     },
33958
33959     /**
33960      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33961      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33962      */
33963     setCollapsedTitle : function(title){
33964         title = title || "&#160;";
33965         if(this.collapsedTitleTextEl){
33966             this.collapsedTitleTextEl.innerHTML = title;
33967         }
33968     },
33969
33970     getBox : function(){
33971         var b;
33972         if(!this.collapsed){
33973             b = this.el.getBox(false, true);
33974         }else{
33975             b = this.collapsedEl.getBox(false, true);
33976         }
33977         return b;
33978     },
33979
33980     getMargins : function(){
33981         return this.collapsed ? this.cmargins : this.margins;
33982     },
33983
33984     highlight : function(){
33985         this.el.addClass("x-layout-panel-dragover");
33986     },
33987
33988     unhighlight : function(){
33989         this.el.removeClass("x-layout-panel-dragover");
33990     },
33991
33992     updateBox : function(box){
33993         this.box = box;
33994         if(!this.collapsed){
33995             this.el.dom.style.left = box.x + "px";
33996             this.el.dom.style.top = box.y + "px";
33997             this.updateBody(box.width, box.height);
33998         }else{
33999             this.collapsedEl.dom.style.left = box.x + "px";
34000             this.collapsedEl.dom.style.top = box.y + "px";
34001             this.collapsedEl.setSize(box.width, box.height);
34002         }
34003         if(this.tabs){
34004             this.tabs.autoSizeTabs();
34005         }
34006     },
34007
34008     updateBody : function(w, h){
34009         if(w !== null){
34010             this.el.setWidth(w);
34011             w -= this.el.getBorderWidth("rl");
34012             if(this.config.adjustments){
34013                 w += this.config.adjustments[0];
34014             }
34015         }
34016         if(h !== null){
34017             this.el.setHeight(h);
34018             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34019             h -= this.el.getBorderWidth("tb");
34020             if(this.config.adjustments){
34021                 h += this.config.adjustments[1];
34022             }
34023             this.bodyEl.setHeight(h);
34024             if(this.tabs){
34025                 h = this.tabs.syncHeight(h);
34026             }
34027         }
34028         if(this.panelSize){
34029             w = w !== null ? w : this.panelSize.width;
34030             h = h !== null ? h : this.panelSize.height;
34031         }
34032         if(this.activePanel){
34033             var el = this.activePanel.getEl();
34034             w = w !== null ? w : el.getWidth();
34035             h = h !== null ? h : el.getHeight();
34036             this.panelSize = {width: w, height: h};
34037             this.activePanel.setSize(w, h);
34038         }
34039         if(Roo.isIE && this.tabs){
34040             this.tabs.el.repaint();
34041         }
34042     },
34043
34044     /**
34045      * Returns the container element for this region.
34046      * @return {Roo.Element}
34047      */
34048     getEl : function(){
34049         return this.el;
34050     },
34051
34052     /**
34053      * Hides this region.
34054      */
34055     hide : function(){
34056         if(!this.collapsed){
34057             this.el.dom.style.left = "-2000px";
34058             this.el.hide();
34059         }else{
34060             this.collapsedEl.dom.style.left = "-2000px";
34061             this.collapsedEl.hide();
34062         }
34063         this.visible = false;
34064         this.fireEvent("visibilitychange", this, false);
34065     },
34066
34067     /**
34068      * Shows this region if it was previously hidden.
34069      */
34070     show : function(){
34071         if(!this.collapsed){
34072             this.el.show();
34073         }else{
34074             this.collapsedEl.show();
34075         }
34076         this.visible = true;
34077         this.fireEvent("visibilitychange", this, true);
34078     },
34079
34080     closeClicked : function(){
34081         if(this.activePanel){
34082             this.remove(this.activePanel);
34083         }
34084     },
34085
34086     collapseClick : function(e){
34087         if(this.isSlid){
34088            e.stopPropagation();
34089            this.slideIn();
34090         }else{
34091            e.stopPropagation();
34092            this.slideOut();
34093         }
34094     },
34095
34096     /**
34097      * Collapses this region.
34098      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34099      */
34100     collapse : function(skipAnim){
34101         if(this.collapsed) {
34102             return;
34103         }
34104         this.collapsed = true;
34105         if(this.split){
34106             this.split.el.hide();
34107         }
34108         if(this.config.animate && skipAnim !== true){
34109             this.fireEvent("invalidated", this);
34110             this.animateCollapse();
34111         }else{
34112             this.el.setLocation(-20000,-20000);
34113             this.el.hide();
34114             this.collapsedEl.show();
34115             this.fireEvent("collapsed", this);
34116             this.fireEvent("invalidated", this);
34117         }
34118     },
34119
34120     animateCollapse : function(){
34121         // overridden
34122     },
34123
34124     /**
34125      * Expands this region if it was previously collapsed.
34126      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34127      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34128      */
34129     expand : function(e, skipAnim){
34130         if(e) {
34131             e.stopPropagation();
34132         }
34133         if(!this.collapsed || this.el.hasActiveFx()) {
34134             return;
34135         }
34136         if(this.isSlid){
34137             this.afterSlideIn();
34138             skipAnim = true;
34139         }
34140         this.collapsed = false;
34141         if(this.config.animate && skipAnim !== true){
34142             this.animateExpand();
34143         }else{
34144             this.el.show();
34145             if(this.split){
34146                 this.split.el.show();
34147             }
34148             this.collapsedEl.setLocation(-2000,-2000);
34149             this.collapsedEl.hide();
34150             this.fireEvent("invalidated", this);
34151             this.fireEvent("expanded", this);
34152         }
34153     },
34154
34155     animateExpand : function(){
34156         // overridden
34157     },
34158
34159     initTabs : function()
34160     {
34161         this.bodyEl.setStyle("overflow", "hidden");
34162         var ts = new Roo.TabPanel(
34163                 this.bodyEl.dom,
34164                 {
34165                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34166                     disableTooltips: this.config.disableTabTips,
34167                     toolbar : this.config.toolbar
34168                 }
34169         );
34170         if(this.config.hideTabs){
34171             ts.stripWrap.setDisplayed(false);
34172         }
34173         this.tabs = ts;
34174         ts.resizeTabs = this.config.resizeTabs === true;
34175         ts.minTabWidth = this.config.minTabWidth || 40;
34176         ts.maxTabWidth = this.config.maxTabWidth || 250;
34177         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34178         ts.monitorResize = false;
34179         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34180         ts.bodyEl.addClass('x-layout-tabs-body');
34181         this.panels.each(this.initPanelAsTab, this);
34182     },
34183
34184     initPanelAsTab : function(panel){
34185         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34186                     this.config.closeOnTab && panel.isClosable());
34187         if(panel.tabTip !== undefined){
34188             ti.setTooltip(panel.tabTip);
34189         }
34190         ti.on("activate", function(){
34191               this.setActivePanel(panel);
34192         }, this);
34193         if(this.config.closeOnTab){
34194             ti.on("beforeclose", function(t, e){
34195                 e.cancel = true;
34196                 this.remove(panel);
34197             }, this);
34198         }
34199         return ti;
34200     },
34201
34202     updatePanelTitle : function(panel, title){
34203         if(this.activePanel == panel){
34204             this.updateTitle(title);
34205         }
34206         if(this.tabs){
34207             var ti = this.tabs.getTab(panel.getEl().id);
34208             ti.setText(title);
34209             if(panel.tabTip !== undefined){
34210                 ti.setTooltip(panel.tabTip);
34211             }
34212         }
34213     },
34214
34215     updateTitle : function(title){
34216         if(this.titleTextEl && !this.config.title){
34217             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34218         }
34219     },
34220
34221     setActivePanel : function(panel){
34222         panel = this.getPanel(panel);
34223         if(this.activePanel && this.activePanel != panel){
34224             this.activePanel.setActiveState(false);
34225         }
34226         this.activePanel = panel;
34227         panel.setActiveState(true);
34228         if(this.panelSize){
34229             panel.setSize(this.panelSize.width, this.panelSize.height);
34230         }
34231         if(this.closeBtn){
34232             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34233         }
34234         this.updateTitle(panel.getTitle());
34235         if(this.tabs){
34236             this.fireEvent("invalidated", this);
34237         }
34238         this.fireEvent("panelactivated", this, panel);
34239     },
34240
34241     /**
34242      * Shows the specified panel.
34243      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34244      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34245      */
34246     showPanel : function(panel)
34247     {
34248         panel = this.getPanel(panel);
34249         if(panel){
34250             if(this.tabs){
34251                 var tab = this.tabs.getTab(panel.getEl().id);
34252                 if(tab.isHidden()){
34253                     this.tabs.unhideTab(tab.id);
34254                 }
34255                 tab.activate();
34256             }else{
34257                 this.setActivePanel(panel);
34258             }
34259         }
34260         return panel;
34261     },
34262
34263     /**
34264      * Get the active panel for this region.
34265      * @return {Roo.ContentPanel} The active panel or null
34266      */
34267     getActivePanel : function(){
34268         return this.activePanel;
34269     },
34270
34271     validateVisibility : function(){
34272         if(this.panels.getCount() < 1){
34273             this.updateTitle("&#160;");
34274             this.closeBtn.hide();
34275             this.hide();
34276         }else{
34277             if(!this.isVisible()){
34278                 this.show();
34279             }
34280         }
34281     },
34282
34283     /**
34284      * Adds the passed ContentPanel(s) to this region.
34285      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34286      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34287      */
34288     add : function(panel){
34289         if(arguments.length > 1){
34290             for(var i = 0, len = arguments.length; i < len; i++) {
34291                 this.add(arguments[i]);
34292             }
34293             return null;
34294         }
34295         if(this.hasPanel(panel)){
34296             this.showPanel(panel);
34297             return panel;
34298         }
34299         panel.setRegion(this);
34300         this.panels.add(panel);
34301         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34302             this.bodyEl.dom.appendChild(panel.getEl().dom);
34303             if(panel.background !== true){
34304                 this.setActivePanel(panel);
34305             }
34306             this.fireEvent("paneladded", this, panel);
34307             return panel;
34308         }
34309         if(!this.tabs){
34310             this.initTabs();
34311         }else{
34312             this.initPanelAsTab(panel);
34313         }
34314         if(panel.background !== true){
34315             this.tabs.activate(panel.getEl().id);
34316         }
34317         this.fireEvent("paneladded", this, panel);
34318         return panel;
34319     },
34320
34321     /**
34322      * Hides the tab for the specified panel.
34323      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34324      */
34325     hidePanel : function(panel){
34326         if(this.tabs && (panel = this.getPanel(panel))){
34327             this.tabs.hideTab(panel.getEl().id);
34328         }
34329     },
34330
34331     /**
34332      * Unhides the tab for a previously hidden panel.
34333      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34334      */
34335     unhidePanel : function(panel){
34336         if(this.tabs && (panel = this.getPanel(panel))){
34337             this.tabs.unhideTab(panel.getEl().id);
34338         }
34339     },
34340
34341     clearPanels : function(){
34342         while(this.panels.getCount() > 0){
34343              this.remove(this.panels.first());
34344         }
34345     },
34346
34347     /**
34348      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34349      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34350      * @param {Boolean} preservePanel Overrides the config preservePanel option
34351      * @return {Roo.ContentPanel} The panel that was removed
34352      */
34353     remove : function(panel, preservePanel){
34354         panel = this.getPanel(panel);
34355         if(!panel){
34356             return null;
34357         }
34358         var e = {};
34359         this.fireEvent("beforeremove", this, panel, e);
34360         if(e.cancel === true){
34361             return null;
34362         }
34363         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34364         var panelId = panel.getId();
34365         this.panels.removeKey(panelId);
34366         if(preservePanel){
34367             document.body.appendChild(panel.getEl().dom);
34368         }
34369         if(this.tabs){
34370             this.tabs.removeTab(panel.getEl().id);
34371         }else if (!preservePanel){
34372             this.bodyEl.dom.removeChild(panel.getEl().dom);
34373         }
34374         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34375             var p = this.panels.first();
34376             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34377             tempEl.appendChild(p.getEl().dom);
34378             this.bodyEl.update("");
34379             this.bodyEl.dom.appendChild(p.getEl().dom);
34380             tempEl = null;
34381             this.updateTitle(p.getTitle());
34382             this.tabs = null;
34383             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34384             this.setActivePanel(p);
34385         }
34386         panel.setRegion(null);
34387         if(this.activePanel == panel){
34388             this.activePanel = null;
34389         }
34390         if(this.config.autoDestroy !== false && preservePanel !== true){
34391             try{panel.destroy();}catch(e){}
34392         }
34393         this.fireEvent("panelremoved", this, panel);
34394         return panel;
34395     },
34396
34397     /**
34398      * Returns the TabPanel component used by this region
34399      * @return {Roo.TabPanel}
34400      */
34401     getTabs : function(){
34402         return this.tabs;
34403     },
34404
34405     createTool : function(parentEl, className){
34406         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34407             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34408         btn.addClassOnOver("x-layout-tools-button-over");
34409         return btn;
34410     }
34411 });/*
34412  * Based on:
34413  * Ext JS Library 1.1.1
34414  * Copyright(c) 2006-2007, Ext JS, LLC.
34415  *
34416  * Originally Released Under LGPL - original licence link has changed is not relivant.
34417  *
34418  * Fork - LGPL
34419  * <script type="text/javascript">
34420  */
34421  
34422
34423
34424 /**
34425  * @class Roo.SplitLayoutRegion
34426  * @extends Roo.LayoutRegion
34427  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34428  */
34429 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34430     this.cursor = cursor;
34431     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34432 };
34433
34434 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34435     splitTip : "Drag to resize.",
34436     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34437     useSplitTips : false,
34438
34439     applyConfig : function(config){
34440         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34441         if(config.split){
34442             if(!this.split){
34443                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34444                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34445                 /** The SplitBar for this region 
34446                 * @type Roo.SplitBar */
34447                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34448                 this.split.on("moved", this.onSplitMove, this);
34449                 this.split.useShim = config.useShim === true;
34450                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34451                 if(this.useSplitTips){
34452                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34453                 }
34454                 if(config.collapsible){
34455                     this.split.el.on("dblclick", this.collapse,  this);
34456                 }
34457             }
34458             if(typeof config.minSize != "undefined"){
34459                 this.split.minSize = config.minSize;
34460             }
34461             if(typeof config.maxSize != "undefined"){
34462                 this.split.maxSize = config.maxSize;
34463             }
34464             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34465                 this.hideSplitter();
34466             }
34467         }
34468     },
34469
34470     getHMaxSize : function(){
34471          var cmax = this.config.maxSize || 10000;
34472          var center = this.mgr.getRegion("center");
34473          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34474     },
34475
34476     getVMaxSize : function(){
34477          var cmax = this.config.maxSize || 10000;
34478          var center = this.mgr.getRegion("center");
34479          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34480     },
34481
34482     onSplitMove : function(split, newSize){
34483         this.fireEvent("resized", this, newSize);
34484     },
34485     
34486     /** 
34487      * Returns the {@link Roo.SplitBar} for this region.
34488      * @return {Roo.SplitBar}
34489      */
34490     getSplitBar : function(){
34491         return this.split;
34492     },
34493     
34494     hide : function(){
34495         this.hideSplitter();
34496         Roo.SplitLayoutRegion.superclass.hide.call(this);
34497     },
34498
34499     hideSplitter : function(){
34500         if(this.split){
34501             this.split.el.setLocation(-2000,-2000);
34502             this.split.el.hide();
34503         }
34504     },
34505
34506     show : function(){
34507         if(this.split){
34508             this.split.el.show();
34509         }
34510         Roo.SplitLayoutRegion.superclass.show.call(this);
34511     },
34512     
34513     beforeSlide: function(){
34514         if(Roo.isGecko){// firefox overflow auto bug workaround
34515             this.bodyEl.clip();
34516             if(this.tabs) {
34517                 this.tabs.bodyEl.clip();
34518             }
34519             if(this.activePanel){
34520                 this.activePanel.getEl().clip();
34521                 
34522                 if(this.activePanel.beforeSlide){
34523                     this.activePanel.beforeSlide();
34524                 }
34525             }
34526         }
34527     },
34528     
34529     afterSlide : function(){
34530         if(Roo.isGecko){// firefox overflow auto bug workaround
34531             this.bodyEl.unclip();
34532             if(this.tabs) {
34533                 this.tabs.bodyEl.unclip();
34534             }
34535             if(this.activePanel){
34536                 this.activePanel.getEl().unclip();
34537                 if(this.activePanel.afterSlide){
34538                     this.activePanel.afterSlide();
34539                 }
34540             }
34541         }
34542     },
34543
34544     initAutoHide : function(){
34545         if(this.autoHide !== false){
34546             if(!this.autoHideHd){
34547                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34548                 this.autoHideHd = {
34549                     "mouseout": function(e){
34550                         if(!e.within(this.el, true)){
34551                             st.delay(500);
34552                         }
34553                     },
34554                     "mouseover" : function(e){
34555                         st.cancel();
34556                     },
34557                     scope : this
34558                 };
34559             }
34560             this.el.on(this.autoHideHd);
34561         }
34562     },
34563
34564     clearAutoHide : function(){
34565         if(this.autoHide !== false){
34566             this.el.un("mouseout", this.autoHideHd.mouseout);
34567             this.el.un("mouseover", this.autoHideHd.mouseover);
34568         }
34569     },
34570
34571     clearMonitor : function(){
34572         Roo.get(document).un("click", this.slideInIf, this);
34573     },
34574
34575     // these names are backwards but not changed for compat
34576     slideOut : function(){
34577         if(this.isSlid || this.el.hasActiveFx()){
34578             return;
34579         }
34580         this.isSlid = true;
34581         if(this.collapseBtn){
34582             this.collapseBtn.hide();
34583         }
34584         this.closeBtnState = this.closeBtn.getStyle('display');
34585         this.closeBtn.hide();
34586         if(this.stickBtn){
34587             this.stickBtn.show();
34588         }
34589         this.el.show();
34590         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34591         this.beforeSlide();
34592         this.el.setStyle("z-index", 10001);
34593         this.el.slideIn(this.getSlideAnchor(), {
34594             callback: function(){
34595                 this.afterSlide();
34596                 this.initAutoHide();
34597                 Roo.get(document).on("click", this.slideInIf, this);
34598                 this.fireEvent("slideshow", this);
34599             },
34600             scope: this,
34601             block: true
34602         });
34603     },
34604
34605     afterSlideIn : function(){
34606         this.clearAutoHide();
34607         this.isSlid = false;
34608         this.clearMonitor();
34609         this.el.setStyle("z-index", "");
34610         if(this.collapseBtn){
34611             this.collapseBtn.show();
34612         }
34613         this.closeBtn.setStyle('display', this.closeBtnState);
34614         if(this.stickBtn){
34615             this.stickBtn.hide();
34616         }
34617         this.fireEvent("slidehide", this);
34618     },
34619
34620     slideIn : function(cb){
34621         if(!this.isSlid || this.el.hasActiveFx()){
34622             Roo.callback(cb);
34623             return;
34624         }
34625         this.isSlid = false;
34626         this.beforeSlide();
34627         this.el.slideOut(this.getSlideAnchor(), {
34628             callback: function(){
34629                 this.el.setLeftTop(-10000, -10000);
34630                 this.afterSlide();
34631                 this.afterSlideIn();
34632                 Roo.callback(cb);
34633             },
34634             scope: this,
34635             block: true
34636         });
34637     },
34638     
34639     slideInIf : function(e){
34640         if(!e.within(this.el)){
34641             this.slideIn();
34642         }
34643     },
34644
34645     animateCollapse : function(){
34646         this.beforeSlide();
34647         this.el.setStyle("z-index", 20000);
34648         var anchor = this.getSlideAnchor();
34649         this.el.slideOut(anchor, {
34650             callback : function(){
34651                 this.el.setStyle("z-index", "");
34652                 this.collapsedEl.slideIn(anchor, {duration:.3});
34653                 this.afterSlide();
34654                 this.el.setLocation(-10000,-10000);
34655                 this.el.hide();
34656                 this.fireEvent("collapsed", this);
34657             },
34658             scope: this,
34659             block: true
34660         });
34661     },
34662
34663     animateExpand : function(){
34664         this.beforeSlide();
34665         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34666         this.el.setStyle("z-index", 20000);
34667         this.collapsedEl.hide({
34668             duration:.1
34669         });
34670         this.el.slideIn(this.getSlideAnchor(), {
34671             callback : function(){
34672                 this.el.setStyle("z-index", "");
34673                 this.afterSlide();
34674                 if(this.split){
34675                     this.split.el.show();
34676                 }
34677                 this.fireEvent("invalidated", this);
34678                 this.fireEvent("expanded", this);
34679             },
34680             scope: this,
34681             block: true
34682         });
34683     },
34684
34685     anchors : {
34686         "west" : "left",
34687         "east" : "right",
34688         "north" : "top",
34689         "south" : "bottom"
34690     },
34691
34692     sanchors : {
34693         "west" : "l",
34694         "east" : "r",
34695         "north" : "t",
34696         "south" : "b"
34697     },
34698
34699     canchors : {
34700         "west" : "tl-tr",
34701         "east" : "tr-tl",
34702         "north" : "tl-bl",
34703         "south" : "bl-tl"
34704     },
34705
34706     getAnchor : function(){
34707         return this.anchors[this.position];
34708     },
34709
34710     getCollapseAnchor : function(){
34711         return this.canchors[this.position];
34712     },
34713
34714     getSlideAnchor : function(){
34715         return this.sanchors[this.position];
34716     },
34717
34718     getAlignAdj : function(){
34719         var cm = this.cmargins;
34720         switch(this.position){
34721             case "west":
34722                 return [0, 0];
34723             break;
34724             case "east":
34725                 return [0, 0];
34726             break;
34727             case "north":
34728                 return [0, 0];
34729             break;
34730             case "south":
34731                 return [0, 0];
34732             break;
34733         }
34734     },
34735
34736     getExpandAdj : function(){
34737         var c = this.collapsedEl, cm = this.cmargins;
34738         switch(this.position){
34739             case "west":
34740                 return [-(cm.right+c.getWidth()+cm.left), 0];
34741             break;
34742             case "east":
34743                 return [cm.right+c.getWidth()+cm.left, 0];
34744             break;
34745             case "north":
34746                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34747             break;
34748             case "south":
34749                 return [0, cm.top+cm.bottom+c.getHeight()];
34750             break;
34751         }
34752     }
34753 });/*
34754  * Based on:
34755  * Ext JS Library 1.1.1
34756  * Copyright(c) 2006-2007, Ext JS, LLC.
34757  *
34758  * Originally Released Under LGPL - original licence link has changed is not relivant.
34759  *
34760  * Fork - LGPL
34761  * <script type="text/javascript">
34762  */
34763 /*
34764  * These classes are private internal classes
34765  */
34766 Roo.CenterLayoutRegion = function(mgr, config){
34767     Roo.LayoutRegion.call(this, mgr, config, "center");
34768     this.visible = true;
34769     this.minWidth = config.minWidth || 20;
34770     this.minHeight = config.minHeight || 20;
34771 };
34772
34773 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34774     hide : function(){
34775         // center panel can't be hidden
34776     },
34777     
34778     show : function(){
34779         // center panel can't be hidden
34780     },
34781     
34782     getMinWidth: function(){
34783         return this.minWidth;
34784     },
34785     
34786     getMinHeight: function(){
34787         return this.minHeight;
34788     }
34789 });
34790
34791
34792 Roo.NorthLayoutRegion = function(mgr, config){
34793     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34794     if(this.split){
34795         this.split.placement = Roo.SplitBar.TOP;
34796         this.split.orientation = Roo.SplitBar.VERTICAL;
34797         this.split.el.addClass("x-layout-split-v");
34798     }
34799     var size = config.initialSize || config.height;
34800     if(typeof size != "undefined"){
34801         this.el.setHeight(size);
34802     }
34803 };
34804 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34805     orientation: Roo.SplitBar.VERTICAL,
34806     getBox : function(){
34807         if(this.collapsed){
34808             return this.collapsedEl.getBox();
34809         }
34810         var box = this.el.getBox();
34811         if(this.split){
34812             box.height += this.split.el.getHeight();
34813         }
34814         return box;
34815     },
34816     
34817     updateBox : function(box){
34818         if(this.split && !this.collapsed){
34819             box.height -= this.split.el.getHeight();
34820             this.split.el.setLeft(box.x);
34821             this.split.el.setTop(box.y+box.height);
34822             this.split.el.setWidth(box.width);
34823         }
34824         if(this.collapsed){
34825             this.updateBody(box.width, null);
34826         }
34827         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34828     }
34829 });
34830
34831 Roo.SouthLayoutRegion = function(mgr, config){
34832     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34833     if(this.split){
34834         this.split.placement = Roo.SplitBar.BOTTOM;
34835         this.split.orientation = Roo.SplitBar.VERTICAL;
34836         this.split.el.addClass("x-layout-split-v");
34837     }
34838     var size = config.initialSize || config.height;
34839     if(typeof size != "undefined"){
34840         this.el.setHeight(size);
34841     }
34842 };
34843 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34844     orientation: Roo.SplitBar.VERTICAL,
34845     getBox : function(){
34846         if(this.collapsed){
34847             return this.collapsedEl.getBox();
34848         }
34849         var box = this.el.getBox();
34850         if(this.split){
34851             var sh = this.split.el.getHeight();
34852             box.height += sh;
34853             box.y -= sh;
34854         }
34855         return box;
34856     },
34857     
34858     updateBox : function(box){
34859         if(this.split && !this.collapsed){
34860             var sh = this.split.el.getHeight();
34861             box.height -= sh;
34862             box.y += sh;
34863             this.split.el.setLeft(box.x);
34864             this.split.el.setTop(box.y-sh);
34865             this.split.el.setWidth(box.width);
34866         }
34867         if(this.collapsed){
34868             this.updateBody(box.width, null);
34869         }
34870         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34871     }
34872 });
34873
34874 Roo.EastLayoutRegion = function(mgr, config){
34875     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34876     if(this.split){
34877         this.split.placement = Roo.SplitBar.RIGHT;
34878         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34879         this.split.el.addClass("x-layout-split-h");
34880     }
34881     var size = config.initialSize || config.width;
34882     if(typeof size != "undefined"){
34883         this.el.setWidth(size);
34884     }
34885 };
34886 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34887     orientation: Roo.SplitBar.HORIZONTAL,
34888     getBox : function(){
34889         if(this.collapsed){
34890             return this.collapsedEl.getBox();
34891         }
34892         var box = this.el.getBox();
34893         if(this.split){
34894             var sw = this.split.el.getWidth();
34895             box.width += sw;
34896             box.x -= sw;
34897         }
34898         return box;
34899     },
34900
34901     updateBox : function(box){
34902         if(this.split && !this.collapsed){
34903             var sw = this.split.el.getWidth();
34904             box.width -= sw;
34905             this.split.el.setLeft(box.x);
34906             this.split.el.setTop(box.y);
34907             this.split.el.setHeight(box.height);
34908             box.x += sw;
34909         }
34910         if(this.collapsed){
34911             this.updateBody(null, box.height);
34912         }
34913         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34914     }
34915 });
34916
34917 Roo.WestLayoutRegion = function(mgr, config){
34918     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34919     if(this.split){
34920         this.split.placement = Roo.SplitBar.LEFT;
34921         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34922         this.split.el.addClass("x-layout-split-h");
34923     }
34924     var size = config.initialSize || config.width;
34925     if(typeof size != "undefined"){
34926         this.el.setWidth(size);
34927     }
34928 };
34929 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34930     orientation: Roo.SplitBar.HORIZONTAL,
34931     getBox : function(){
34932         if(this.collapsed){
34933             return this.collapsedEl.getBox();
34934         }
34935         var box = this.el.getBox();
34936         if(this.split){
34937             box.width += this.split.el.getWidth();
34938         }
34939         return box;
34940     },
34941     
34942     updateBox : function(box){
34943         if(this.split && !this.collapsed){
34944             var sw = this.split.el.getWidth();
34945             box.width -= sw;
34946             this.split.el.setLeft(box.x+box.width);
34947             this.split.el.setTop(box.y);
34948             this.split.el.setHeight(box.height);
34949         }
34950         if(this.collapsed){
34951             this.updateBody(null, box.height);
34952         }
34953         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34954     }
34955 });
34956 /*
34957  * Based on:
34958  * Ext JS Library 1.1.1
34959  * Copyright(c) 2006-2007, Ext JS, LLC.
34960  *
34961  * Originally Released Under LGPL - original licence link has changed is not relivant.
34962  *
34963  * Fork - LGPL
34964  * <script type="text/javascript">
34965  */
34966  
34967  
34968 /*
34969  * Private internal class for reading and applying state
34970  */
34971 Roo.LayoutStateManager = function(layout){
34972      // default empty state
34973      this.state = {
34974         north: {},
34975         south: {},
34976         east: {},
34977         west: {}       
34978     };
34979 };
34980
34981 Roo.LayoutStateManager.prototype = {
34982     init : function(layout, provider){
34983         this.provider = provider;
34984         var state = provider.get(layout.id+"-layout-state");
34985         if(state){
34986             var wasUpdating = layout.isUpdating();
34987             if(!wasUpdating){
34988                 layout.beginUpdate();
34989             }
34990             for(var key in state){
34991                 if(typeof state[key] != "function"){
34992                     var rstate = state[key];
34993                     var r = layout.getRegion(key);
34994                     if(r && rstate){
34995                         if(rstate.size){
34996                             r.resizeTo(rstate.size);
34997                         }
34998                         if(rstate.collapsed == true){
34999                             r.collapse(true);
35000                         }else{
35001                             r.expand(null, true);
35002                         }
35003                     }
35004                 }
35005             }
35006             if(!wasUpdating){
35007                 layout.endUpdate();
35008             }
35009             this.state = state; 
35010         }
35011         this.layout = layout;
35012         layout.on("regionresized", this.onRegionResized, this);
35013         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35014         layout.on("regionexpanded", this.onRegionExpanded, this);
35015     },
35016     
35017     storeState : function(){
35018         this.provider.set(this.layout.id+"-layout-state", this.state);
35019     },
35020     
35021     onRegionResized : function(region, newSize){
35022         this.state[region.getPosition()].size = newSize;
35023         this.storeState();
35024     },
35025     
35026     onRegionCollapsed : function(region){
35027         this.state[region.getPosition()].collapsed = true;
35028         this.storeState();
35029     },
35030     
35031     onRegionExpanded : function(region){
35032         this.state[region.getPosition()].collapsed = false;
35033         this.storeState();
35034     }
35035 };/*
35036  * Based on:
35037  * Ext JS Library 1.1.1
35038  * Copyright(c) 2006-2007, Ext JS, LLC.
35039  *
35040  * Originally Released Under LGPL - original licence link has changed is not relivant.
35041  *
35042  * Fork - LGPL
35043  * <script type="text/javascript">
35044  */
35045 /**
35046  * @class Roo.ContentPanel
35047  * @extends Roo.util.Observable
35048  * A basic ContentPanel element.
35049  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35050  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35051  * @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
35052  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35053  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35054  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35055  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35056  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35057  * @cfg {String} title          The title for this panel
35058  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35059  * @cfg {String} url            Calls {@link #setUrl} with this value
35060  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35061  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35062  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35063  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35064
35065  * @constructor
35066  * Create a new ContentPanel.
35067  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35068  * @param {String/Object} config A string to set only the title or a config object
35069  * @param {String} content (optional) Set the HTML content for this panel
35070  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35071  */
35072 Roo.ContentPanel = function(el, config, content){
35073     
35074      
35075     /*
35076     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35077         config = el;
35078         el = Roo.id();
35079     }
35080     if (config && config.parentLayout) { 
35081         el = config.parentLayout.el.createChild(); 
35082     }
35083     */
35084     if(el.autoCreate){ // xtype is available if this is called from factory
35085         config = el;
35086         el = Roo.id();
35087     }
35088     this.el = Roo.get(el);
35089     if(!this.el && config && config.autoCreate){
35090         if(typeof config.autoCreate == "object"){
35091             if(!config.autoCreate.id){
35092                 config.autoCreate.id = config.id||el;
35093             }
35094             this.el = Roo.DomHelper.append(document.body,
35095                         config.autoCreate, true);
35096         }else{
35097             this.el = Roo.DomHelper.append(document.body,
35098                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35099         }
35100     }
35101     this.closable = false;
35102     this.loaded = false;
35103     this.active = false;
35104     if(typeof config == "string"){
35105         this.title = config;
35106     }else{
35107         Roo.apply(this, config);
35108     }
35109     
35110     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35111         this.wrapEl = this.el.wrap();
35112         this.toolbar.container = this.el.insertSibling(false, 'before');
35113         this.toolbar = new Roo.Toolbar(this.toolbar);
35114     }
35115     
35116     // xtype created footer. - not sure if will work as we normally have to render first..
35117     if (this.footer && !this.footer.el && this.footer.xtype) {
35118         if (!this.wrapEl) {
35119             this.wrapEl = this.el.wrap();
35120         }
35121     
35122         this.footer.container = this.wrapEl.createChild();
35123          
35124         this.footer = Roo.factory(this.footer, Roo);
35125         
35126     }
35127     
35128     if(this.resizeEl){
35129         this.resizeEl = Roo.get(this.resizeEl, true);
35130     }else{
35131         this.resizeEl = this.el;
35132     }
35133     // handle view.xtype
35134     
35135  
35136     
35137     
35138     this.addEvents({
35139         /**
35140          * @event activate
35141          * Fires when this panel is activated. 
35142          * @param {Roo.ContentPanel} this
35143          */
35144         "activate" : true,
35145         /**
35146          * @event deactivate
35147          * Fires when this panel is activated. 
35148          * @param {Roo.ContentPanel} this
35149          */
35150         "deactivate" : true,
35151
35152         /**
35153          * @event resize
35154          * Fires when this panel is resized if fitToFrame is true.
35155          * @param {Roo.ContentPanel} this
35156          * @param {Number} width The width after any component adjustments
35157          * @param {Number} height The height after any component adjustments
35158          */
35159         "resize" : true,
35160         
35161          /**
35162          * @event render
35163          * Fires when this tab is created
35164          * @param {Roo.ContentPanel} this
35165          */
35166         "render" : true
35167         
35168         
35169         
35170     });
35171     
35172
35173     
35174     
35175     if(this.autoScroll){
35176         this.resizeEl.setStyle("overflow", "auto");
35177     } else {
35178         // fix randome scrolling
35179         this.el.on('scroll', function() {
35180             Roo.log('fix random scolling');
35181             this.scrollTo('top',0); 
35182         });
35183     }
35184     content = content || this.content;
35185     if(content){
35186         this.setContent(content);
35187     }
35188     if(config && config.url){
35189         this.setUrl(this.url, this.params, this.loadOnce);
35190     }
35191     
35192     
35193     
35194     Roo.ContentPanel.superclass.constructor.call(this);
35195     
35196     if (this.view && typeof(this.view.xtype) != 'undefined') {
35197         this.view.el = this.el.appendChild(document.createElement("div"));
35198         this.view = Roo.factory(this.view); 
35199         this.view.render  &&  this.view.render(false, '');  
35200     }
35201     
35202     
35203     this.fireEvent('render', this);
35204 };
35205
35206 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35207     tabTip:'',
35208     setRegion : function(region){
35209         this.region = region;
35210         if(region){
35211            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35212         }else{
35213            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35214         } 
35215     },
35216     
35217     /**
35218      * Returns the toolbar for this Panel if one was configured. 
35219      * @return {Roo.Toolbar} 
35220      */
35221     getToolbar : function(){
35222         return this.toolbar;
35223     },
35224     
35225     setActiveState : function(active){
35226         this.active = active;
35227         if(!active){
35228             this.fireEvent("deactivate", this);
35229         }else{
35230             this.fireEvent("activate", this);
35231         }
35232     },
35233     /**
35234      * Updates this panel's element
35235      * @param {String} content The new content
35236      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35237     */
35238     setContent : function(content, loadScripts){
35239         this.el.update(content, loadScripts);
35240     },
35241
35242     ignoreResize : function(w, h){
35243         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35244             return true;
35245         }else{
35246             this.lastSize = {width: w, height: h};
35247             return false;
35248         }
35249     },
35250     /**
35251      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35252      * @return {Roo.UpdateManager} The UpdateManager
35253      */
35254     getUpdateManager : function(){
35255         return this.el.getUpdateManager();
35256     },
35257      /**
35258      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35259      * @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:
35260 <pre><code>
35261 panel.load({
35262     url: "your-url.php",
35263     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35264     callback: yourFunction,
35265     scope: yourObject, //(optional scope)
35266     discardUrl: false,
35267     nocache: false,
35268     text: "Loading...",
35269     timeout: 30,
35270     scripts: false
35271 });
35272 </code></pre>
35273      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35274      * 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.
35275      * @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}
35276      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35277      * @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.
35278      * @return {Roo.ContentPanel} this
35279      */
35280     load : function(){
35281         var um = this.el.getUpdateManager();
35282         um.update.apply(um, arguments);
35283         return this;
35284     },
35285
35286
35287     /**
35288      * 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.
35289      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35290      * @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)
35291      * @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)
35292      * @return {Roo.UpdateManager} The UpdateManager
35293      */
35294     setUrl : function(url, params, loadOnce){
35295         if(this.refreshDelegate){
35296             this.removeListener("activate", this.refreshDelegate);
35297         }
35298         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35299         this.on("activate", this.refreshDelegate);
35300         return this.el.getUpdateManager();
35301     },
35302     
35303     _handleRefresh : function(url, params, loadOnce){
35304         if(!loadOnce || !this.loaded){
35305             var updater = this.el.getUpdateManager();
35306             updater.update(url, params, this._setLoaded.createDelegate(this));
35307         }
35308     },
35309     
35310     _setLoaded : function(){
35311         this.loaded = true;
35312     }, 
35313     
35314     /**
35315      * Returns this panel's id
35316      * @return {String} 
35317      */
35318     getId : function(){
35319         return this.el.id;
35320     },
35321     
35322     /** 
35323      * Returns this panel's element - used by regiosn to add.
35324      * @return {Roo.Element} 
35325      */
35326     getEl : function(){
35327         return this.wrapEl || this.el;
35328     },
35329     
35330     adjustForComponents : function(width, height)
35331     {
35332         //Roo.log('adjustForComponents ');
35333         if(this.resizeEl != this.el){
35334             width -= this.el.getFrameWidth('lr');
35335             height -= this.el.getFrameWidth('tb');
35336         }
35337         if(this.toolbar){
35338             var te = this.toolbar.getEl();
35339             height -= te.getHeight();
35340             te.setWidth(width);
35341         }
35342         if(this.footer){
35343             var te = this.footer.getEl();
35344             Roo.log("footer:" + te.getHeight());
35345             
35346             height -= te.getHeight();
35347             te.setWidth(width);
35348         }
35349         
35350         
35351         if(this.adjustments){
35352             width += this.adjustments[0];
35353             height += this.adjustments[1];
35354         }
35355         return {"width": width, "height": height};
35356     },
35357     
35358     setSize : function(width, height){
35359         if(this.fitToFrame && !this.ignoreResize(width, height)){
35360             if(this.fitContainer && this.resizeEl != this.el){
35361                 this.el.setSize(width, height);
35362             }
35363             var size = this.adjustForComponents(width, height);
35364             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35365             this.fireEvent('resize', this, size.width, size.height);
35366         }
35367     },
35368     
35369     /**
35370      * Returns this panel's title
35371      * @return {String} 
35372      */
35373     getTitle : function(){
35374         return this.title;
35375     },
35376     
35377     /**
35378      * Set this panel's title
35379      * @param {String} title
35380      */
35381     setTitle : function(title){
35382         this.title = title;
35383         if(this.region){
35384             this.region.updatePanelTitle(this, title);
35385         }
35386     },
35387     
35388     /**
35389      * Returns true is this panel was configured to be closable
35390      * @return {Boolean} 
35391      */
35392     isClosable : function(){
35393         return this.closable;
35394     },
35395     
35396     beforeSlide : function(){
35397         this.el.clip();
35398         this.resizeEl.clip();
35399     },
35400     
35401     afterSlide : function(){
35402         this.el.unclip();
35403         this.resizeEl.unclip();
35404     },
35405     
35406     /**
35407      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35408      *   Will fail silently if the {@link #setUrl} method has not been called.
35409      *   This does not activate the panel, just updates its content.
35410      */
35411     refresh : function(){
35412         if(this.refreshDelegate){
35413            this.loaded = false;
35414            this.refreshDelegate();
35415         }
35416     },
35417     
35418     /**
35419      * Destroys this panel
35420      */
35421     destroy : function(){
35422         this.el.removeAllListeners();
35423         var tempEl = document.createElement("span");
35424         tempEl.appendChild(this.el.dom);
35425         tempEl.innerHTML = "";
35426         this.el.remove();
35427         this.el = null;
35428     },
35429     
35430     /**
35431      * form - if the content panel contains a form - this is a reference to it.
35432      * @type {Roo.form.Form}
35433      */
35434     form : false,
35435     /**
35436      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35437      *    This contains a reference to it.
35438      * @type {Roo.View}
35439      */
35440     view : false,
35441     
35442       /**
35443      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35444      * <pre><code>
35445
35446 layout.addxtype({
35447        xtype : 'Form',
35448        items: [ .... ]
35449    }
35450 );
35451
35452 </code></pre>
35453      * @param {Object} cfg Xtype definition of item to add.
35454      */
35455     
35456     addxtype : function(cfg) {
35457         // add form..
35458         if (cfg.xtype.match(/^Form$/)) {
35459             
35460             var el;
35461             //if (this.footer) {
35462             //    el = this.footer.container.insertSibling(false, 'before');
35463             //} else {
35464                 el = this.el.createChild();
35465             //}
35466
35467             this.form = new  Roo.form.Form(cfg);
35468             
35469             
35470             if ( this.form.allItems.length) {
35471                 this.form.render(el.dom);
35472             }
35473             return this.form;
35474         }
35475         // should only have one of theses..
35476         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35477             // views.. should not be just added - used named prop 'view''
35478             
35479             cfg.el = this.el.appendChild(document.createElement("div"));
35480             // factory?
35481             
35482             var ret = new Roo.factory(cfg);
35483              
35484              ret.render && ret.render(false, ''); // render blank..
35485             this.view = ret;
35486             return ret;
35487         }
35488         return false;
35489     }
35490 });
35491
35492 /**
35493  * @class Roo.GridPanel
35494  * @extends Roo.ContentPanel
35495  * @constructor
35496  * Create a new GridPanel.
35497  * @param {Roo.grid.Grid} grid The grid for this panel
35498  * @param {String/Object} config A string to set only the panel's title, or a config object
35499  */
35500 Roo.GridPanel = function(grid, config){
35501     
35502   
35503     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35504         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35505         
35506     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35507     
35508     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35509     
35510     if(this.toolbar){
35511         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35512     }
35513     // xtype created footer. - not sure if will work as we normally have to render first..
35514     if (this.footer && !this.footer.el && this.footer.xtype) {
35515         
35516         this.footer.container = this.grid.getView().getFooterPanel(true);
35517         this.footer.dataSource = this.grid.dataSource;
35518         this.footer = Roo.factory(this.footer, Roo);
35519         
35520     }
35521     
35522     grid.monitorWindowResize = false; // turn off autosizing
35523     grid.autoHeight = false;
35524     grid.autoWidth = false;
35525     this.grid = grid;
35526     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35527 };
35528
35529 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35530     getId : function(){
35531         return this.grid.id;
35532     },
35533     
35534     /**
35535      * Returns the grid for this panel
35536      * @return {Roo.grid.Grid} 
35537      */
35538     getGrid : function(){
35539         return this.grid;    
35540     },
35541     
35542     setSize : function(width, height){
35543         if(!this.ignoreResize(width, height)){
35544             var grid = this.grid;
35545             var size = this.adjustForComponents(width, height);
35546             grid.getGridEl().setSize(size.width, size.height);
35547             grid.autoSize();
35548         }
35549     },
35550     
35551     beforeSlide : function(){
35552         this.grid.getView().scroller.clip();
35553     },
35554     
35555     afterSlide : function(){
35556         this.grid.getView().scroller.unclip();
35557     },
35558     
35559     destroy : function(){
35560         this.grid.destroy();
35561         delete this.grid;
35562         Roo.GridPanel.superclass.destroy.call(this); 
35563     }
35564 });
35565
35566
35567 /**
35568  * @class Roo.NestedLayoutPanel
35569  * @extends Roo.ContentPanel
35570  * @constructor
35571  * Create a new NestedLayoutPanel.
35572  * 
35573  * 
35574  * @param {Roo.BorderLayout} layout The layout for this panel
35575  * @param {String/Object} config A string to set only the title or a config object
35576  */
35577 Roo.NestedLayoutPanel = function(layout, config)
35578 {
35579     // construct with only one argument..
35580     /* FIXME - implement nicer consturctors
35581     if (layout.layout) {
35582         config = layout;
35583         layout = config.layout;
35584         delete config.layout;
35585     }
35586     if (layout.xtype && !layout.getEl) {
35587         // then layout needs constructing..
35588         layout = Roo.factory(layout, Roo);
35589     }
35590     */
35591     
35592     
35593     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35594     
35595     layout.monitorWindowResize = false; // turn off autosizing
35596     this.layout = layout;
35597     this.layout.getEl().addClass("x-layout-nested-layout");
35598     
35599     
35600     
35601     
35602 };
35603
35604 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35605
35606     setSize : function(width, height){
35607         if(!this.ignoreResize(width, height)){
35608             var size = this.adjustForComponents(width, height);
35609             var el = this.layout.getEl();
35610             el.setSize(size.width, size.height);
35611             var touch = el.dom.offsetWidth;
35612             this.layout.layout();
35613             // ie requires a double layout on the first pass
35614             if(Roo.isIE && !this.initialized){
35615                 this.initialized = true;
35616                 this.layout.layout();
35617             }
35618         }
35619     },
35620     
35621     // activate all subpanels if not currently active..
35622     
35623     setActiveState : function(active){
35624         this.active = active;
35625         if(!active){
35626             this.fireEvent("deactivate", this);
35627             return;
35628         }
35629         
35630         this.fireEvent("activate", this);
35631         // not sure if this should happen before or after..
35632         if (!this.layout) {
35633             return; // should not happen..
35634         }
35635         var reg = false;
35636         for (var r in this.layout.regions) {
35637             reg = this.layout.getRegion(r);
35638             if (reg.getActivePanel()) {
35639                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35640                 reg.setActivePanel(reg.getActivePanel());
35641                 continue;
35642             }
35643             if (!reg.panels.length) {
35644                 continue;
35645             }
35646             reg.showPanel(reg.getPanel(0));
35647         }
35648         
35649         
35650         
35651         
35652     },
35653     
35654     /**
35655      * Returns the nested BorderLayout for this panel
35656      * @return {Roo.BorderLayout} 
35657      */
35658     getLayout : function(){
35659         return this.layout;
35660     },
35661     
35662      /**
35663      * Adds a xtype elements to the layout of the nested panel
35664      * <pre><code>
35665
35666 panel.addxtype({
35667        xtype : 'ContentPanel',
35668        region: 'west',
35669        items: [ .... ]
35670    }
35671 );
35672
35673 panel.addxtype({
35674         xtype : 'NestedLayoutPanel',
35675         region: 'west',
35676         layout: {
35677            center: { },
35678            west: { }   
35679         },
35680         items : [ ... list of content panels or nested layout panels.. ]
35681    }
35682 );
35683 </code></pre>
35684      * @param {Object} cfg Xtype definition of item to add.
35685      */
35686     addxtype : function(cfg) {
35687         return this.layout.addxtype(cfg);
35688     
35689     }
35690 });
35691
35692 Roo.ScrollPanel = function(el, config, content){
35693     config = config || {};
35694     config.fitToFrame = true;
35695     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35696     
35697     this.el.dom.style.overflow = "hidden";
35698     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35699     this.el.removeClass("x-layout-inactive-content");
35700     this.el.on("mousewheel", this.onWheel, this);
35701
35702     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35703     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35704     up.unselectable(); down.unselectable();
35705     up.on("click", this.scrollUp, this);
35706     down.on("click", this.scrollDown, this);
35707     up.addClassOnOver("x-scroller-btn-over");
35708     down.addClassOnOver("x-scroller-btn-over");
35709     up.addClassOnClick("x-scroller-btn-click");
35710     down.addClassOnClick("x-scroller-btn-click");
35711     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35712
35713     this.resizeEl = this.el;
35714     this.el = wrap; this.up = up; this.down = down;
35715 };
35716
35717 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35718     increment : 100,
35719     wheelIncrement : 5,
35720     scrollUp : function(){
35721         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35722     },
35723
35724     scrollDown : function(){
35725         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35726     },
35727
35728     afterScroll : function(){
35729         var el = this.resizeEl;
35730         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35731         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35732         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35733     },
35734
35735     setSize : function(){
35736         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35737         this.afterScroll();
35738     },
35739
35740     onWheel : function(e){
35741         var d = e.getWheelDelta();
35742         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35743         this.afterScroll();
35744         e.stopEvent();
35745     },
35746
35747     setContent : function(content, loadScripts){
35748         this.resizeEl.update(content, loadScripts);
35749     }
35750
35751 });
35752
35753
35754
35755
35756
35757
35758
35759
35760
35761 /**
35762  * @class Roo.TreePanel
35763  * @extends Roo.ContentPanel
35764  * @constructor
35765  * Create a new TreePanel. - defaults to fit/scoll contents.
35766  * @param {String/Object} config A string to set only the panel's title, or a config object
35767  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35768  */
35769 Roo.TreePanel = function(config){
35770     var el = config.el;
35771     var tree = config.tree;
35772     delete config.tree; 
35773     delete config.el; // hopefull!
35774     
35775     // wrapper for IE7 strict & safari scroll issue
35776     
35777     var treeEl = el.createChild();
35778     config.resizeEl = treeEl;
35779     
35780     
35781     
35782     Roo.TreePanel.superclass.constructor.call(this, el, config);
35783  
35784  
35785     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35786     //console.log(tree);
35787     this.on('activate', function()
35788     {
35789         if (this.tree.rendered) {
35790             return;
35791         }
35792         //console.log('render tree');
35793         this.tree.render();
35794     });
35795     // this should not be needed.. - it's actually the 'el' that resizes?
35796     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35797     
35798     //this.on('resize',  function (cp, w, h) {
35799     //        this.tree.innerCt.setWidth(w);
35800     //        this.tree.innerCt.setHeight(h);
35801     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35802     //});
35803
35804         
35805     
35806 };
35807
35808 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35809     fitToFrame : true,
35810     autoScroll : true
35811 });
35812
35813
35814
35815
35816
35817
35818
35819
35820
35821
35822
35823 /*
35824  * Based on:
35825  * Ext JS Library 1.1.1
35826  * Copyright(c) 2006-2007, Ext JS, LLC.
35827  *
35828  * Originally Released Under LGPL - original licence link has changed is not relivant.
35829  *
35830  * Fork - LGPL
35831  * <script type="text/javascript">
35832  */
35833  
35834
35835 /**
35836  * @class Roo.ReaderLayout
35837  * @extends Roo.BorderLayout
35838  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35839  * center region containing two nested regions (a top one for a list view and one for item preview below),
35840  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35841  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35842  * expedites the setup of the overall layout and regions for this common application style.
35843  * Example:
35844  <pre><code>
35845 var reader = new Roo.ReaderLayout();
35846 var CP = Roo.ContentPanel;  // shortcut for adding
35847
35848 reader.beginUpdate();
35849 reader.add("north", new CP("north", "North"));
35850 reader.add("west", new CP("west", {title: "West"}));
35851 reader.add("east", new CP("east", {title: "East"}));
35852
35853 reader.regions.listView.add(new CP("listView", "List"));
35854 reader.regions.preview.add(new CP("preview", "Preview"));
35855 reader.endUpdate();
35856 </code></pre>
35857 * @constructor
35858 * Create a new ReaderLayout
35859 * @param {Object} config Configuration options
35860 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35861 * document.body if omitted)
35862 */
35863 Roo.ReaderLayout = function(config, renderTo){
35864     var c = config || {size:{}};
35865     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35866         north: c.north !== false ? Roo.apply({
35867             split:false,
35868             initialSize: 32,
35869             titlebar: false
35870         }, c.north) : false,
35871         west: c.west !== false ? Roo.apply({
35872             split:true,
35873             initialSize: 200,
35874             minSize: 175,
35875             maxSize: 400,
35876             titlebar: true,
35877             collapsible: true,
35878             animate: true,
35879             margins:{left:5,right:0,bottom:5,top:5},
35880             cmargins:{left:5,right:5,bottom:5,top:5}
35881         }, c.west) : false,
35882         east: c.east !== false ? Roo.apply({
35883             split:true,
35884             initialSize: 200,
35885             minSize: 175,
35886             maxSize: 400,
35887             titlebar: true,
35888             collapsible: true,
35889             animate: true,
35890             margins:{left:0,right:5,bottom:5,top:5},
35891             cmargins:{left:5,right:5,bottom:5,top:5}
35892         }, c.east) : false,
35893         center: Roo.apply({
35894             tabPosition: 'top',
35895             autoScroll:false,
35896             closeOnTab: true,
35897             titlebar:false,
35898             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35899         }, c.center)
35900     });
35901
35902     this.el.addClass('x-reader');
35903
35904     this.beginUpdate();
35905
35906     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35907         south: c.preview !== false ? Roo.apply({
35908             split:true,
35909             initialSize: 200,
35910             minSize: 100,
35911             autoScroll:true,
35912             collapsible:true,
35913             titlebar: true,
35914             cmargins:{top:5,left:0, right:0, bottom:0}
35915         }, c.preview) : false,
35916         center: Roo.apply({
35917             autoScroll:false,
35918             titlebar:false,
35919             minHeight:200
35920         }, c.listView)
35921     });
35922     this.add('center', new Roo.NestedLayoutPanel(inner,
35923             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35924
35925     this.endUpdate();
35926
35927     this.regions.preview = inner.getRegion('south');
35928     this.regions.listView = inner.getRegion('center');
35929 };
35930
35931 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35932  * Based on:
35933  * Ext JS Library 1.1.1
35934  * Copyright(c) 2006-2007, Ext JS, LLC.
35935  *
35936  * Originally Released Under LGPL - original licence link has changed is not relivant.
35937  *
35938  * Fork - LGPL
35939  * <script type="text/javascript">
35940  */
35941  
35942 /**
35943  * @class Roo.grid.Grid
35944  * @extends Roo.util.Observable
35945  * This class represents the primary interface of a component based grid control.
35946  * <br><br>Usage:<pre><code>
35947  var grid = new Roo.grid.Grid("my-container-id", {
35948      ds: myDataStore,
35949      cm: myColModel,
35950      selModel: mySelectionModel,
35951      autoSizeColumns: true,
35952      monitorWindowResize: false,
35953      trackMouseOver: true
35954  });
35955  // set any options
35956  grid.render();
35957  * </code></pre>
35958  * <b>Common Problems:</b><br/>
35959  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35960  * element will correct this<br/>
35961  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35962  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35963  * are unpredictable.<br/>
35964  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35965  * grid to calculate dimensions/offsets.<br/>
35966   * @constructor
35967  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35968  * The container MUST have some type of size defined for the grid to fill. The container will be
35969  * automatically set to position relative if it isn't already.
35970  * @param {Object} config A config object that sets properties on this grid.
35971  */
35972 Roo.grid.Grid = function(container, config){
35973         // initialize the container
35974         this.container = Roo.get(container);
35975         this.container.update("");
35976         this.container.setStyle("overflow", "hidden");
35977     this.container.addClass('x-grid-container');
35978
35979     this.id = this.container.id;
35980
35981     Roo.apply(this, config);
35982     // check and correct shorthanded configs
35983     if(this.ds){
35984         this.dataSource = this.ds;
35985         delete this.ds;
35986     }
35987     if(this.cm){
35988         this.colModel = this.cm;
35989         delete this.cm;
35990     }
35991     if(this.sm){
35992         this.selModel = this.sm;
35993         delete this.sm;
35994     }
35995
35996     if (this.selModel) {
35997         this.selModel = Roo.factory(this.selModel, Roo.grid);
35998         this.sm = this.selModel;
35999         this.sm.xmodule = this.xmodule || false;
36000     }
36001     if (typeof(this.colModel.config) == 'undefined') {
36002         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36003         this.cm = this.colModel;
36004         this.cm.xmodule = this.xmodule || false;
36005     }
36006     if (this.dataSource) {
36007         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36008         this.ds = this.dataSource;
36009         this.ds.xmodule = this.xmodule || false;
36010          
36011     }
36012     
36013     
36014     
36015     if(this.width){
36016         this.container.setWidth(this.width);
36017     }
36018
36019     if(this.height){
36020         this.container.setHeight(this.height);
36021     }
36022     /** @private */
36023         this.addEvents({
36024         // raw events
36025         /**
36026          * @event click
36027          * The raw click event for the entire grid.
36028          * @param {Roo.EventObject} e
36029          */
36030         "click" : true,
36031         /**
36032          * @event dblclick
36033          * The raw dblclick event for the entire grid.
36034          * @param {Roo.EventObject} e
36035          */
36036         "dblclick" : true,
36037         /**
36038          * @event contextmenu
36039          * The raw contextmenu event for the entire grid.
36040          * @param {Roo.EventObject} e
36041          */
36042         "contextmenu" : true,
36043         /**
36044          * @event mousedown
36045          * The raw mousedown event for the entire grid.
36046          * @param {Roo.EventObject} e
36047          */
36048         "mousedown" : true,
36049         /**
36050          * @event mouseup
36051          * The raw mouseup event for the entire grid.
36052          * @param {Roo.EventObject} e
36053          */
36054         "mouseup" : true,
36055         /**
36056          * @event mouseover
36057          * The raw mouseover event for the entire grid.
36058          * @param {Roo.EventObject} e
36059          */
36060         "mouseover" : true,
36061         /**
36062          * @event mouseout
36063          * The raw mouseout event for the entire grid.
36064          * @param {Roo.EventObject} e
36065          */
36066         "mouseout" : true,
36067         /**
36068          * @event keypress
36069          * The raw keypress event for the entire grid.
36070          * @param {Roo.EventObject} e
36071          */
36072         "keypress" : true,
36073         /**
36074          * @event keydown
36075          * The raw keydown event for the entire grid.
36076          * @param {Roo.EventObject} e
36077          */
36078         "keydown" : true,
36079
36080         // custom events
36081
36082         /**
36083          * @event cellclick
36084          * Fires when a cell is clicked
36085          * @param {Grid} this
36086          * @param {Number} rowIndex
36087          * @param {Number} columnIndex
36088          * @param {Roo.EventObject} e
36089          */
36090         "cellclick" : true,
36091         /**
36092          * @event celldblclick
36093          * Fires when a cell is double clicked
36094          * @param {Grid} this
36095          * @param {Number} rowIndex
36096          * @param {Number} columnIndex
36097          * @param {Roo.EventObject} e
36098          */
36099         "celldblclick" : true,
36100         /**
36101          * @event rowclick
36102          * Fires when a row is clicked
36103          * @param {Grid} this
36104          * @param {Number} rowIndex
36105          * @param {Roo.EventObject} e
36106          */
36107         "rowclick" : true,
36108         /**
36109          * @event rowdblclick
36110          * Fires when a row is double clicked
36111          * @param {Grid} this
36112          * @param {Number} rowIndex
36113          * @param {Roo.EventObject} e
36114          */
36115         "rowdblclick" : true,
36116         /**
36117          * @event headerclick
36118          * Fires when a header is clicked
36119          * @param {Grid} this
36120          * @param {Number} columnIndex
36121          * @param {Roo.EventObject} e
36122          */
36123         "headerclick" : true,
36124         /**
36125          * @event headerdblclick
36126          * Fires when a header cell is double clicked
36127          * @param {Grid} this
36128          * @param {Number} columnIndex
36129          * @param {Roo.EventObject} e
36130          */
36131         "headerdblclick" : true,
36132         /**
36133          * @event rowcontextmenu
36134          * Fires when a row is right clicked
36135          * @param {Grid} this
36136          * @param {Number} rowIndex
36137          * @param {Roo.EventObject} e
36138          */
36139         "rowcontextmenu" : true,
36140         /**
36141          * @event cellcontextmenu
36142          * Fires when a cell is right clicked
36143          * @param {Grid} this
36144          * @param {Number} rowIndex
36145          * @param {Number} cellIndex
36146          * @param {Roo.EventObject} e
36147          */
36148          "cellcontextmenu" : true,
36149         /**
36150          * @event headercontextmenu
36151          * Fires when a header is right clicked
36152          * @param {Grid} this
36153          * @param {Number} columnIndex
36154          * @param {Roo.EventObject} e
36155          */
36156         "headercontextmenu" : true,
36157         /**
36158          * @event bodyscroll
36159          * Fires when the body element is scrolled
36160          * @param {Number} scrollLeft
36161          * @param {Number} scrollTop
36162          */
36163         "bodyscroll" : true,
36164         /**
36165          * @event columnresize
36166          * Fires when the user resizes a column
36167          * @param {Number} columnIndex
36168          * @param {Number} newSize
36169          */
36170         "columnresize" : true,
36171         /**
36172          * @event columnmove
36173          * Fires when the user moves a column
36174          * @param {Number} oldIndex
36175          * @param {Number} newIndex
36176          */
36177         "columnmove" : true,
36178         /**
36179          * @event startdrag
36180          * Fires when row(s) start being dragged
36181          * @param {Grid} this
36182          * @param {Roo.GridDD} dd The drag drop object
36183          * @param {event} e The raw browser event
36184          */
36185         "startdrag" : true,
36186         /**
36187          * @event enddrag
36188          * Fires when a drag operation is complete
36189          * @param {Grid} this
36190          * @param {Roo.GridDD} dd The drag drop object
36191          * @param {event} e The raw browser event
36192          */
36193         "enddrag" : true,
36194         /**
36195          * @event dragdrop
36196          * Fires when dragged row(s) are dropped on a valid DD target
36197          * @param {Grid} this
36198          * @param {Roo.GridDD} dd The drag drop object
36199          * @param {String} targetId The target drag drop object
36200          * @param {event} e The raw browser event
36201          */
36202         "dragdrop" : true,
36203         /**
36204          * @event dragover
36205          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36206          * @param {Grid} this
36207          * @param {Roo.GridDD} dd The drag drop object
36208          * @param {String} targetId The target drag drop object
36209          * @param {event} e The raw browser event
36210          */
36211         "dragover" : true,
36212         /**
36213          * @event dragenter
36214          *  Fires when the dragged row(s) first cross another DD target while being dragged
36215          * @param {Grid} this
36216          * @param {Roo.GridDD} dd The drag drop object
36217          * @param {String} targetId The target drag drop object
36218          * @param {event} e The raw browser event
36219          */
36220         "dragenter" : true,
36221         /**
36222          * @event dragout
36223          * Fires when the dragged row(s) leave another DD target while being dragged
36224          * @param {Grid} this
36225          * @param {Roo.GridDD} dd The drag drop object
36226          * @param {String} targetId The target drag drop object
36227          * @param {event} e The raw browser event
36228          */
36229         "dragout" : true,
36230         /**
36231          * @event rowclass
36232          * Fires when a row is rendered, so you can change add a style to it.
36233          * @param {GridView} gridview   The grid view
36234          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36235          */
36236         'rowclass' : true,
36237
36238         /**
36239          * @event render
36240          * Fires when the grid is rendered
36241          * @param {Grid} grid
36242          */
36243         'render' : true
36244     });
36245
36246     Roo.grid.Grid.superclass.constructor.call(this);
36247 };
36248 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36249     
36250     /**
36251      * @cfg {String} ddGroup - drag drop group.
36252      */
36253
36254     /**
36255      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36256      */
36257     minColumnWidth : 25,
36258
36259     /**
36260      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36261      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36262      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36263      */
36264     autoSizeColumns : false,
36265
36266     /**
36267      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36268      */
36269     autoSizeHeaders : true,
36270
36271     /**
36272      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36273      */
36274     monitorWindowResize : true,
36275
36276     /**
36277      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36278      * rows measured to get a columns size. Default is 0 (all rows).
36279      */
36280     maxRowsToMeasure : 0,
36281
36282     /**
36283      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36284      */
36285     trackMouseOver : true,
36286
36287     /**
36288     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36289     */
36290     
36291     /**
36292     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36293     */
36294     enableDragDrop : false,
36295     
36296     /**
36297     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36298     */
36299     enableColumnMove : true,
36300     
36301     /**
36302     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36303     */
36304     enableColumnHide : true,
36305     
36306     /**
36307     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36308     */
36309     enableRowHeightSync : false,
36310     
36311     /**
36312     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36313     */
36314     stripeRows : true,
36315     
36316     /**
36317     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36318     */
36319     autoHeight : false,
36320
36321     /**
36322      * @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.
36323      */
36324     autoExpandColumn : false,
36325
36326     /**
36327     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36328     * Default is 50.
36329     */
36330     autoExpandMin : 50,
36331
36332     /**
36333     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36334     */
36335     autoExpandMax : 1000,
36336
36337     /**
36338     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36339     */
36340     view : null,
36341
36342     /**
36343     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36344     */
36345     loadMask : false,
36346     /**
36347     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36348     */
36349     dropTarget: false,
36350     
36351    
36352     
36353     // private
36354     rendered : false,
36355
36356     /**
36357     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36358     * of a fixed width. Default is false.
36359     */
36360     /**
36361     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36362     */
36363     /**
36364      * Called once after all setup has been completed and the grid is ready to be rendered.
36365      * @return {Roo.grid.Grid} this
36366      */
36367     render : function()
36368     {
36369         var c = this.container;
36370         // try to detect autoHeight/width mode
36371         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36372             this.autoHeight = true;
36373         }
36374         var view = this.getView();
36375         view.init(this);
36376
36377         c.on("click", this.onClick, this);
36378         c.on("dblclick", this.onDblClick, this);
36379         c.on("contextmenu", this.onContextMenu, this);
36380         c.on("keydown", this.onKeyDown, this);
36381         if (Roo.isTouch) {
36382             c.on("touchstart", this.onTouchStart, this);
36383         }
36384
36385         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36386
36387         this.getSelectionModel().init(this);
36388
36389         view.render();
36390
36391         if(this.loadMask){
36392             this.loadMask = new Roo.LoadMask(this.container,
36393                     Roo.apply({store:this.dataSource}, this.loadMask));
36394         }
36395         
36396         
36397         if (this.toolbar && this.toolbar.xtype) {
36398             this.toolbar.container = this.getView().getHeaderPanel(true);
36399             this.toolbar = new Roo.Toolbar(this.toolbar);
36400         }
36401         if (this.footer && this.footer.xtype) {
36402             this.footer.dataSource = this.getDataSource();
36403             this.footer.container = this.getView().getFooterPanel(true);
36404             this.footer = Roo.factory(this.footer, Roo);
36405         }
36406         if (this.dropTarget && this.dropTarget.xtype) {
36407             delete this.dropTarget.xtype;
36408             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36409         }
36410         
36411         
36412         this.rendered = true;
36413         this.fireEvent('render', this);
36414         return this;
36415     },
36416
36417         /**
36418          * Reconfigures the grid to use a different Store and Column Model.
36419          * The View will be bound to the new objects and refreshed.
36420          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36421          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36422          */
36423     reconfigure : function(dataSource, colModel){
36424         if(this.loadMask){
36425             this.loadMask.destroy();
36426             this.loadMask = new Roo.LoadMask(this.container,
36427                     Roo.apply({store:dataSource}, this.loadMask));
36428         }
36429         this.view.bind(dataSource, colModel);
36430         this.dataSource = dataSource;
36431         this.colModel = colModel;
36432         this.view.refresh(true);
36433     },
36434
36435     // private
36436     onKeyDown : function(e){
36437         this.fireEvent("keydown", e);
36438     },
36439
36440     /**
36441      * Destroy this grid.
36442      * @param {Boolean} removeEl True to remove the element
36443      */
36444     destroy : function(removeEl, keepListeners){
36445         if(this.loadMask){
36446             this.loadMask.destroy();
36447         }
36448         var c = this.container;
36449         c.removeAllListeners();
36450         this.view.destroy();
36451         this.colModel.purgeListeners();
36452         if(!keepListeners){
36453             this.purgeListeners();
36454         }
36455         c.update("");
36456         if(removeEl === true){
36457             c.remove();
36458         }
36459     },
36460
36461     // private
36462     processEvent : function(name, e){
36463         // does this fire select???
36464         //Roo.log('grid:processEvent '  + name);
36465         
36466         if (name != 'touchstart' ) {
36467             this.fireEvent(name, e);    
36468         }
36469         
36470         var t = e.getTarget();
36471         var v = this.view;
36472         var header = v.findHeaderIndex(t);
36473         if(header !== false){
36474             var ename = name == 'touchstart' ? 'click' : name;
36475              
36476             this.fireEvent("header" + ename, this, header, e);
36477         }else{
36478             var row = v.findRowIndex(t);
36479             var cell = v.findCellIndex(t);
36480             if (name == 'touchstart') {
36481                 // first touch is always a click.
36482                 // hopefull this happens after selection is updated.?
36483                 name = false;
36484                 
36485                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36486                     var cs = this.selModel.getSelectedCell();
36487                     if (row == cs[0] && cell == cs[1]){
36488                         name = 'dblclick';
36489                     }
36490                 }
36491                 if (typeof(this.selModel.getSelections) != 'undefined') {
36492                     var cs = this.selModel.getSelections();
36493                     var ds = this.dataSource;
36494                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36495                         name = 'dblclick';
36496                     }
36497                 }
36498                 if (!name) {
36499                     return;
36500                 }
36501             }
36502             
36503             
36504             if(row !== false){
36505                 this.fireEvent("row" + name, this, row, e);
36506                 if(cell !== false){
36507                     this.fireEvent("cell" + name, this, row, cell, e);
36508                 }
36509             }
36510         }
36511     },
36512
36513     // private
36514     onClick : function(e){
36515         this.processEvent("click", e);
36516     },
36517    // private
36518     onTouchStart : function(e){
36519         this.processEvent("touchstart", e);
36520     },
36521
36522     // private
36523     onContextMenu : function(e, t){
36524         this.processEvent("contextmenu", e);
36525     },
36526
36527     // private
36528     onDblClick : function(e){
36529         this.processEvent("dblclick", e);
36530     },
36531
36532     // private
36533     walkCells : function(row, col, step, fn, scope){
36534         var cm = this.colModel, clen = cm.getColumnCount();
36535         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36536         if(step < 0){
36537             if(col < 0){
36538                 row--;
36539                 first = false;
36540             }
36541             while(row >= 0){
36542                 if(!first){
36543                     col = clen-1;
36544                 }
36545                 first = false;
36546                 while(col >= 0){
36547                     if(fn.call(scope || this, row, col, cm) === true){
36548                         return [row, col];
36549                     }
36550                     col--;
36551                 }
36552                 row--;
36553             }
36554         } else {
36555             if(col >= clen){
36556                 row++;
36557                 first = false;
36558             }
36559             while(row < rlen){
36560                 if(!first){
36561                     col = 0;
36562                 }
36563                 first = false;
36564                 while(col < clen){
36565                     if(fn.call(scope || this, row, col, cm) === true){
36566                         return [row, col];
36567                     }
36568                     col++;
36569                 }
36570                 row++;
36571             }
36572         }
36573         return null;
36574     },
36575
36576     // private
36577     getSelections : function(){
36578         return this.selModel.getSelections();
36579     },
36580
36581     /**
36582      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36583      * but if manual update is required this method will initiate it.
36584      */
36585     autoSize : function(){
36586         if(this.rendered){
36587             this.view.layout();
36588             if(this.view.adjustForScroll){
36589                 this.view.adjustForScroll();
36590             }
36591         }
36592     },
36593
36594     /**
36595      * Returns the grid's underlying element.
36596      * @return {Element} The element
36597      */
36598     getGridEl : function(){
36599         return this.container;
36600     },
36601
36602     // private for compatibility, overridden by editor grid
36603     stopEditing : function(){},
36604
36605     /**
36606      * Returns the grid's SelectionModel.
36607      * @return {SelectionModel}
36608      */
36609     getSelectionModel : function(){
36610         if(!this.selModel){
36611             this.selModel = new Roo.grid.RowSelectionModel();
36612         }
36613         return this.selModel;
36614     },
36615
36616     /**
36617      * Returns the grid's DataSource.
36618      * @return {DataSource}
36619      */
36620     getDataSource : function(){
36621         return this.dataSource;
36622     },
36623
36624     /**
36625      * Returns the grid's ColumnModel.
36626      * @return {ColumnModel}
36627      */
36628     getColumnModel : function(){
36629         return this.colModel;
36630     },
36631
36632     /**
36633      * Returns the grid's GridView object.
36634      * @return {GridView}
36635      */
36636     getView : function(){
36637         if(!this.view){
36638             this.view = new Roo.grid.GridView(this.viewConfig);
36639         }
36640         return this.view;
36641     },
36642     /**
36643      * Called to get grid's drag proxy text, by default returns this.ddText.
36644      * @return {String}
36645      */
36646     getDragDropText : function(){
36647         var count = this.selModel.getCount();
36648         return String.format(this.ddText, count, count == 1 ? '' : 's');
36649     }
36650 });
36651 /**
36652  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36653  * %0 is replaced with the number of selected rows.
36654  * @type String
36655  */
36656 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36657  * Based on:
36658  * Ext JS Library 1.1.1
36659  * Copyright(c) 2006-2007, Ext JS, LLC.
36660  *
36661  * Originally Released Under LGPL - original licence link has changed is not relivant.
36662  *
36663  * Fork - LGPL
36664  * <script type="text/javascript">
36665  */
36666  
36667 Roo.grid.AbstractGridView = function(){
36668         this.grid = null;
36669         
36670         this.events = {
36671             "beforerowremoved" : true,
36672             "beforerowsinserted" : true,
36673             "beforerefresh" : true,
36674             "rowremoved" : true,
36675             "rowsinserted" : true,
36676             "rowupdated" : true,
36677             "refresh" : true
36678         };
36679     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36680 };
36681
36682 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36683     rowClass : "x-grid-row",
36684     cellClass : "x-grid-cell",
36685     tdClass : "x-grid-td",
36686     hdClass : "x-grid-hd",
36687     splitClass : "x-grid-hd-split",
36688     
36689     init: function(grid){
36690         this.grid = grid;
36691                 var cid = this.grid.getGridEl().id;
36692         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36693         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36694         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36695         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36696         },
36697         
36698     getColumnRenderers : function(){
36699         var renderers = [];
36700         var cm = this.grid.colModel;
36701         var colCount = cm.getColumnCount();
36702         for(var i = 0; i < colCount; i++){
36703             renderers[i] = cm.getRenderer(i);
36704         }
36705         return renderers;
36706     },
36707     
36708     getColumnIds : function(){
36709         var ids = [];
36710         var cm = this.grid.colModel;
36711         var colCount = cm.getColumnCount();
36712         for(var i = 0; i < colCount; i++){
36713             ids[i] = cm.getColumnId(i);
36714         }
36715         return ids;
36716     },
36717     
36718     getDataIndexes : function(){
36719         if(!this.indexMap){
36720             this.indexMap = this.buildIndexMap();
36721         }
36722         return this.indexMap.colToData;
36723     },
36724     
36725     getColumnIndexByDataIndex : function(dataIndex){
36726         if(!this.indexMap){
36727             this.indexMap = this.buildIndexMap();
36728         }
36729         return this.indexMap.dataToCol[dataIndex];
36730     },
36731     
36732     /**
36733      * Set a css style for a column dynamically. 
36734      * @param {Number} colIndex The index of the column
36735      * @param {String} name The css property name
36736      * @param {String} value The css value
36737      */
36738     setCSSStyle : function(colIndex, name, value){
36739         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36740         Roo.util.CSS.updateRule(selector, name, value);
36741     },
36742     
36743     generateRules : function(cm){
36744         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36745         Roo.util.CSS.removeStyleSheet(rulesId);
36746         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36747             var cid = cm.getColumnId(i);
36748             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36749                          this.tdSelector, cid, " {\n}\n",
36750                          this.hdSelector, cid, " {\n}\n",
36751                          this.splitSelector, cid, " {\n}\n");
36752         }
36753         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36754     }
36755 });/*
36756  * Based on:
36757  * Ext JS Library 1.1.1
36758  * Copyright(c) 2006-2007, Ext JS, LLC.
36759  *
36760  * Originally Released Under LGPL - original licence link has changed is not relivant.
36761  *
36762  * Fork - LGPL
36763  * <script type="text/javascript">
36764  */
36765
36766 // private
36767 // This is a support class used internally by the Grid components
36768 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36769     this.grid = grid;
36770     this.view = grid.getView();
36771     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36772     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36773     if(hd2){
36774         this.setHandleElId(Roo.id(hd));
36775         this.setOuterHandleElId(Roo.id(hd2));
36776     }
36777     this.scroll = false;
36778 };
36779 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36780     maxDragWidth: 120,
36781     getDragData : function(e){
36782         var t = Roo.lib.Event.getTarget(e);
36783         var h = this.view.findHeaderCell(t);
36784         if(h){
36785             return {ddel: h.firstChild, header:h};
36786         }
36787         return false;
36788     },
36789
36790     onInitDrag : function(e){
36791         this.view.headersDisabled = true;
36792         var clone = this.dragData.ddel.cloneNode(true);
36793         clone.id = Roo.id();
36794         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36795         this.proxy.update(clone);
36796         return true;
36797     },
36798
36799     afterValidDrop : function(){
36800         var v = this.view;
36801         setTimeout(function(){
36802             v.headersDisabled = false;
36803         }, 50);
36804     },
36805
36806     afterInvalidDrop : function(){
36807         var v = this.view;
36808         setTimeout(function(){
36809             v.headersDisabled = false;
36810         }, 50);
36811     }
36812 });
36813 /*
36814  * Based on:
36815  * Ext JS Library 1.1.1
36816  * Copyright(c) 2006-2007, Ext JS, LLC.
36817  *
36818  * Originally Released Under LGPL - original licence link has changed is not relivant.
36819  *
36820  * Fork - LGPL
36821  * <script type="text/javascript">
36822  */
36823 // private
36824 // This is a support class used internally by the Grid components
36825 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36826     this.grid = grid;
36827     this.view = grid.getView();
36828     // split the proxies so they don't interfere with mouse events
36829     this.proxyTop = Roo.DomHelper.append(document.body, {
36830         cls:"col-move-top", html:"&#160;"
36831     }, true);
36832     this.proxyBottom = Roo.DomHelper.append(document.body, {
36833         cls:"col-move-bottom", html:"&#160;"
36834     }, true);
36835     this.proxyTop.hide = this.proxyBottom.hide = function(){
36836         this.setLeftTop(-100,-100);
36837         this.setStyle("visibility", "hidden");
36838     };
36839     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36840     // temporarily disabled
36841     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36842     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36843 };
36844 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36845     proxyOffsets : [-4, -9],
36846     fly: Roo.Element.fly,
36847
36848     getTargetFromEvent : function(e){
36849         var t = Roo.lib.Event.getTarget(e);
36850         var cindex = this.view.findCellIndex(t);
36851         if(cindex !== false){
36852             return this.view.getHeaderCell(cindex);
36853         }
36854         return null;
36855     },
36856
36857     nextVisible : function(h){
36858         var v = this.view, cm = this.grid.colModel;
36859         h = h.nextSibling;
36860         while(h){
36861             if(!cm.isHidden(v.getCellIndex(h))){
36862                 return h;
36863             }
36864             h = h.nextSibling;
36865         }
36866         return null;
36867     },
36868
36869     prevVisible : function(h){
36870         var v = this.view, cm = this.grid.colModel;
36871         h = h.prevSibling;
36872         while(h){
36873             if(!cm.isHidden(v.getCellIndex(h))){
36874                 return h;
36875             }
36876             h = h.prevSibling;
36877         }
36878         return null;
36879     },
36880
36881     positionIndicator : function(h, n, e){
36882         var x = Roo.lib.Event.getPageX(e);
36883         var r = Roo.lib.Dom.getRegion(n.firstChild);
36884         var px, pt, py = r.top + this.proxyOffsets[1];
36885         if((r.right - x) <= (r.right-r.left)/2){
36886             px = r.right+this.view.borderWidth;
36887             pt = "after";
36888         }else{
36889             px = r.left;
36890             pt = "before";
36891         }
36892         var oldIndex = this.view.getCellIndex(h);
36893         var newIndex = this.view.getCellIndex(n);
36894
36895         if(this.grid.colModel.isFixed(newIndex)){
36896             return false;
36897         }
36898
36899         var locked = this.grid.colModel.isLocked(newIndex);
36900
36901         if(pt == "after"){
36902             newIndex++;
36903         }
36904         if(oldIndex < newIndex){
36905             newIndex--;
36906         }
36907         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36908             return false;
36909         }
36910         px +=  this.proxyOffsets[0];
36911         this.proxyTop.setLeftTop(px, py);
36912         this.proxyTop.show();
36913         if(!this.bottomOffset){
36914             this.bottomOffset = this.view.mainHd.getHeight();
36915         }
36916         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36917         this.proxyBottom.show();
36918         return pt;
36919     },
36920
36921     onNodeEnter : function(n, dd, e, data){
36922         if(data.header != n){
36923             this.positionIndicator(data.header, n, e);
36924         }
36925     },
36926
36927     onNodeOver : function(n, dd, e, data){
36928         var result = false;
36929         if(data.header != n){
36930             result = this.positionIndicator(data.header, n, e);
36931         }
36932         if(!result){
36933             this.proxyTop.hide();
36934             this.proxyBottom.hide();
36935         }
36936         return result ? this.dropAllowed : this.dropNotAllowed;
36937     },
36938
36939     onNodeOut : function(n, dd, e, data){
36940         this.proxyTop.hide();
36941         this.proxyBottom.hide();
36942     },
36943
36944     onNodeDrop : function(n, dd, e, data){
36945         var h = data.header;
36946         if(h != n){
36947             var cm = this.grid.colModel;
36948             var x = Roo.lib.Event.getPageX(e);
36949             var r = Roo.lib.Dom.getRegion(n.firstChild);
36950             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36951             var oldIndex = this.view.getCellIndex(h);
36952             var newIndex = this.view.getCellIndex(n);
36953             var locked = cm.isLocked(newIndex);
36954             if(pt == "after"){
36955                 newIndex++;
36956             }
36957             if(oldIndex < newIndex){
36958                 newIndex--;
36959             }
36960             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36961                 return false;
36962             }
36963             cm.setLocked(oldIndex, locked, true);
36964             cm.moveColumn(oldIndex, newIndex);
36965             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36966             return true;
36967         }
36968         return false;
36969     }
36970 });
36971 /*
36972  * Based on:
36973  * Ext JS Library 1.1.1
36974  * Copyright(c) 2006-2007, Ext JS, LLC.
36975  *
36976  * Originally Released Under LGPL - original licence link has changed is not relivant.
36977  *
36978  * Fork - LGPL
36979  * <script type="text/javascript">
36980  */
36981   
36982 /**
36983  * @class Roo.grid.GridView
36984  * @extends Roo.util.Observable
36985  *
36986  * @constructor
36987  * @param {Object} config
36988  */
36989 Roo.grid.GridView = function(config){
36990     Roo.grid.GridView.superclass.constructor.call(this);
36991     this.el = null;
36992
36993     Roo.apply(this, config);
36994 };
36995
36996 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36997
36998     unselectable :  'unselectable="on"',
36999     unselectableCls :  'x-unselectable',
37000     
37001     
37002     rowClass : "x-grid-row",
37003
37004     cellClass : "x-grid-col",
37005
37006     tdClass : "x-grid-td",
37007
37008     hdClass : "x-grid-hd",
37009
37010     splitClass : "x-grid-split",
37011
37012     sortClasses : ["sort-asc", "sort-desc"],
37013
37014     enableMoveAnim : false,
37015
37016     hlColor: "C3DAF9",
37017
37018     dh : Roo.DomHelper,
37019
37020     fly : Roo.Element.fly,
37021
37022     css : Roo.util.CSS,
37023
37024     borderWidth: 1,
37025
37026     splitOffset: 3,
37027
37028     scrollIncrement : 22,
37029
37030     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37031
37032     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37033
37034     bind : function(ds, cm){
37035         if(this.ds){
37036             this.ds.un("load", this.onLoad, this);
37037             this.ds.un("datachanged", this.onDataChange, this);
37038             this.ds.un("add", this.onAdd, this);
37039             this.ds.un("remove", this.onRemove, this);
37040             this.ds.un("update", this.onUpdate, this);
37041             this.ds.un("clear", this.onClear, this);
37042         }
37043         if(ds){
37044             ds.on("load", this.onLoad, this);
37045             ds.on("datachanged", this.onDataChange, this);
37046             ds.on("add", this.onAdd, this);
37047             ds.on("remove", this.onRemove, this);
37048             ds.on("update", this.onUpdate, this);
37049             ds.on("clear", this.onClear, this);
37050         }
37051         this.ds = ds;
37052
37053         if(this.cm){
37054             this.cm.un("widthchange", this.onColWidthChange, this);
37055             this.cm.un("headerchange", this.onHeaderChange, this);
37056             this.cm.un("hiddenchange", this.onHiddenChange, this);
37057             this.cm.un("columnmoved", this.onColumnMove, this);
37058             this.cm.un("columnlockchange", this.onColumnLock, this);
37059         }
37060         if(cm){
37061             this.generateRules(cm);
37062             cm.on("widthchange", this.onColWidthChange, this);
37063             cm.on("headerchange", this.onHeaderChange, this);
37064             cm.on("hiddenchange", this.onHiddenChange, this);
37065             cm.on("columnmoved", this.onColumnMove, this);
37066             cm.on("columnlockchange", this.onColumnLock, this);
37067         }
37068         this.cm = cm;
37069     },
37070
37071     init: function(grid){
37072         Roo.grid.GridView.superclass.init.call(this, grid);
37073
37074         this.bind(grid.dataSource, grid.colModel);
37075
37076         grid.on("headerclick", this.handleHeaderClick, this);
37077
37078         if(grid.trackMouseOver){
37079             grid.on("mouseover", this.onRowOver, this);
37080             grid.on("mouseout", this.onRowOut, this);
37081         }
37082         grid.cancelTextSelection = function(){};
37083         this.gridId = grid.id;
37084
37085         var tpls = this.templates || {};
37086
37087         if(!tpls.master){
37088             tpls.master = new Roo.Template(
37089                '<div class="x-grid" hidefocus="true">',
37090                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37091                   '<div class="x-grid-topbar"></div>',
37092                   '<div class="x-grid-scroller"><div></div></div>',
37093                   '<div class="x-grid-locked">',
37094                       '<div class="x-grid-header">{lockedHeader}</div>',
37095                       '<div class="x-grid-body">{lockedBody}</div>',
37096                   "</div>",
37097                   '<div class="x-grid-viewport">',
37098                       '<div class="x-grid-header">{header}</div>',
37099                       '<div class="x-grid-body">{body}</div>',
37100                   "</div>",
37101                   '<div class="x-grid-bottombar"></div>',
37102                  
37103                   '<div class="x-grid-resize-proxy">&#160;</div>',
37104                "</div>"
37105             );
37106             tpls.master.disableformats = true;
37107         }
37108
37109         if(!tpls.header){
37110             tpls.header = new Roo.Template(
37111                '<table border="0" cellspacing="0" cellpadding="0">',
37112                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37113                "</table>{splits}"
37114             );
37115             tpls.header.disableformats = true;
37116         }
37117         tpls.header.compile();
37118
37119         if(!tpls.hcell){
37120             tpls.hcell = new Roo.Template(
37121                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37122                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37123                 "</div></td>"
37124              );
37125              tpls.hcell.disableFormats = true;
37126         }
37127         tpls.hcell.compile();
37128
37129         if(!tpls.hsplit){
37130             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37131                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37132             tpls.hsplit.disableFormats = true;
37133         }
37134         tpls.hsplit.compile();
37135
37136         if(!tpls.body){
37137             tpls.body = new Roo.Template(
37138                '<table border="0" cellspacing="0" cellpadding="0">',
37139                "<tbody>{rows}</tbody>",
37140                "</table>"
37141             );
37142             tpls.body.disableFormats = true;
37143         }
37144         tpls.body.compile();
37145
37146         if(!tpls.row){
37147             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37148             tpls.row.disableFormats = true;
37149         }
37150         tpls.row.compile();
37151
37152         if(!tpls.cell){
37153             tpls.cell = new Roo.Template(
37154                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37155                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37156                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37157                 "</td>"
37158             );
37159             tpls.cell.disableFormats = true;
37160         }
37161         tpls.cell.compile();
37162
37163         this.templates = tpls;
37164     },
37165
37166     // remap these for backwards compat
37167     onColWidthChange : function(){
37168         this.updateColumns.apply(this, arguments);
37169     },
37170     onHeaderChange : function(){
37171         this.updateHeaders.apply(this, arguments);
37172     }, 
37173     onHiddenChange : function(){
37174         this.handleHiddenChange.apply(this, arguments);
37175     },
37176     onColumnMove : function(){
37177         this.handleColumnMove.apply(this, arguments);
37178     },
37179     onColumnLock : function(){
37180         this.handleLockChange.apply(this, arguments);
37181     },
37182
37183     onDataChange : function(){
37184         this.refresh();
37185         this.updateHeaderSortState();
37186     },
37187
37188     onClear : function(){
37189         this.refresh();
37190     },
37191
37192     onUpdate : function(ds, record){
37193         this.refreshRow(record);
37194     },
37195
37196     refreshRow : function(record){
37197         var ds = this.ds, index;
37198         if(typeof record == 'number'){
37199             index = record;
37200             record = ds.getAt(index);
37201         }else{
37202             index = ds.indexOf(record);
37203         }
37204         this.insertRows(ds, index, index, true);
37205         this.onRemove(ds, record, index+1, true);
37206         this.syncRowHeights(index, index);
37207         this.layout();
37208         this.fireEvent("rowupdated", this, index, record);
37209     },
37210
37211     onAdd : function(ds, records, index){
37212         this.insertRows(ds, index, index + (records.length-1));
37213     },
37214
37215     onRemove : function(ds, record, index, isUpdate){
37216         if(isUpdate !== true){
37217             this.fireEvent("beforerowremoved", this, index, record);
37218         }
37219         var bt = this.getBodyTable(), lt = this.getLockedTable();
37220         if(bt.rows[index]){
37221             bt.firstChild.removeChild(bt.rows[index]);
37222         }
37223         if(lt.rows[index]){
37224             lt.firstChild.removeChild(lt.rows[index]);
37225         }
37226         if(isUpdate !== true){
37227             this.stripeRows(index);
37228             this.syncRowHeights(index, index);
37229             this.layout();
37230             this.fireEvent("rowremoved", this, index, record);
37231         }
37232     },
37233
37234     onLoad : function(){
37235         this.scrollToTop();
37236     },
37237
37238     /**
37239      * Scrolls the grid to the top
37240      */
37241     scrollToTop : function(){
37242         if(this.scroller){
37243             this.scroller.dom.scrollTop = 0;
37244             this.syncScroll();
37245         }
37246     },
37247
37248     /**
37249      * Gets a panel in the header of the grid that can be used for toolbars etc.
37250      * After modifying the contents of this panel a call to grid.autoSize() may be
37251      * required to register any changes in size.
37252      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37253      * @return Roo.Element
37254      */
37255     getHeaderPanel : function(doShow){
37256         if(doShow){
37257             this.headerPanel.show();
37258         }
37259         return this.headerPanel;
37260     },
37261
37262     /**
37263      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37264      * After modifying the contents of this panel a call to grid.autoSize() may be
37265      * required to register any changes in size.
37266      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37267      * @return Roo.Element
37268      */
37269     getFooterPanel : function(doShow){
37270         if(doShow){
37271             this.footerPanel.show();
37272         }
37273         return this.footerPanel;
37274     },
37275
37276     initElements : function(){
37277         var E = Roo.Element;
37278         var el = this.grid.getGridEl().dom.firstChild;
37279         var cs = el.childNodes;
37280
37281         this.el = new E(el);
37282         
37283          this.focusEl = new E(el.firstChild);
37284         this.focusEl.swallowEvent("click", true);
37285         
37286         this.headerPanel = new E(cs[1]);
37287         this.headerPanel.enableDisplayMode("block");
37288
37289         this.scroller = new E(cs[2]);
37290         this.scrollSizer = new E(this.scroller.dom.firstChild);
37291
37292         this.lockedWrap = new E(cs[3]);
37293         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37294         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37295
37296         this.mainWrap = new E(cs[4]);
37297         this.mainHd = new E(this.mainWrap.dom.firstChild);
37298         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37299
37300         this.footerPanel = new E(cs[5]);
37301         this.footerPanel.enableDisplayMode("block");
37302
37303         this.resizeProxy = new E(cs[6]);
37304
37305         this.headerSelector = String.format(
37306            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37307            this.lockedHd.id, this.mainHd.id
37308         );
37309
37310         this.splitterSelector = String.format(
37311            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37312            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37313         );
37314     },
37315     idToCssName : function(s)
37316     {
37317         return s.replace(/[^a-z0-9]+/ig, '-');
37318     },
37319
37320     getHeaderCell : function(index){
37321         return Roo.DomQuery.select(this.headerSelector)[index];
37322     },
37323
37324     getHeaderCellMeasure : function(index){
37325         return this.getHeaderCell(index).firstChild;
37326     },
37327
37328     getHeaderCellText : function(index){
37329         return this.getHeaderCell(index).firstChild.firstChild;
37330     },
37331
37332     getLockedTable : function(){
37333         return this.lockedBody.dom.firstChild;
37334     },
37335
37336     getBodyTable : function(){
37337         return this.mainBody.dom.firstChild;
37338     },
37339
37340     getLockedRow : function(index){
37341         return this.getLockedTable().rows[index];
37342     },
37343
37344     getRow : function(index){
37345         return this.getBodyTable().rows[index];
37346     },
37347
37348     getRowComposite : function(index){
37349         if(!this.rowEl){
37350             this.rowEl = new Roo.CompositeElementLite();
37351         }
37352         var els = [], lrow, mrow;
37353         if(lrow = this.getLockedRow(index)){
37354             els.push(lrow);
37355         }
37356         if(mrow = this.getRow(index)){
37357             els.push(mrow);
37358         }
37359         this.rowEl.elements = els;
37360         return this.rowEl;
37361     },
37362     /**
37363      * Gets the 'td' of the cell
37364      * 
37365      * @param {Integer} rowIndex row to select
37366      * @param {Integer} colIndex column to select
37367      * 
37368      * @return {Object} 
37369      */
37370     getCell : function(rowIndex, colIndex){
37371         var locked = this.cm.getLockedCount();
37372         var source;
37373         if(colIndex < locked){
37374             source = this.lockedBody.dom.firstChild;
37375         }else{
37376             source = this.mainBody.dom.firstChild;
37377             colIndex -= locked;
37378         }
37379         return source.rows[rowIndex].childNodes[colIndex];
37380     },
37381
37382     getCellText : function(rowIndex, colIndex){
37383         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37384     },
37385
37386     getCellBox : function(cell){
37387         var b = this.fly(cell).getBox();
37388         if(Roo.isOpera){ // opera fails to report the Y
37389             b.y = cell.offsetTop + this.mainBody.getY();
37390         }
37391         return b;
37392     },
37393
37394     getCellIndex : function(cell){
37395         var id = String(cell.className).match(this.cellRE);
37396         if(id){
37397             return parseInt(id[1], 10);
37398         }
37399         return 0;
37400     },
37401
37402     findHeaderIndex : function(n){
37403         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37404         return r ? this.getCellIndex(r) : false;
37405     },
37406
37407     findHeaderCell : function(n){
37408         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37409         return r ? r : false;
37410     },
37411
37412     findRowIndex : function(n){
37413         if(!n){
37414             return false;
37415         }
37416         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37417         return r ? r.rowIndex : false;
37418     },
37419
37420     findCellIndex : function(node){
37421         var stop = this.el.dom;
37422         while(node && node != stop){
37423             if(this.findRE.test(node.className)){
37424                 return this.getCellIndex(node);
37425             }
37426             node = node.parentNode;
37427         }
37428         return false;
37429     },
37430
37431     getColumnId : function(index){
37432         return this.cm.getColumnId(index);
37433     },
37434
37435     getSplitters : function()
37436     {
37437         if(this.splitterSelector){
37438            return Roo.DomQuery.select(this.splitterSelector);
37439         }else{
37440             return null;
37441       }
37442     },
37443
37444     getSplitter : function(index){
37445         return this.getSplitters()[index];
37446     },
37447
37448     onRowOver : function(e, t){
37449         var row;
37450         if((row = this.findRowIndex(t)) !== false){
37451             this.getRowComposite(row).addClass("x-grid-row-over");
37452         }
37453     },
37454
37455     onRowOut : function(e, t){
37456         var row;
37457         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37458             this.getRowComposite(row).removeClass("x-grid-row-over");
37459         }
37460     },
37461
37462     renderHeaders : function(){
37463         var cm = this.cm;
37464         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37465         var cb = [], lb = [], sb = [], lsb = [], p = {};
37466         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37467             p.cellId = "x-grid-hd-0-" + i;
37468             p.splitId = "x-grid-csplit-0-" + i;
37469             p.id = cm.getColumnId(i);
37470             p.title = cm.getColumnTooltip(i) || "";
37471             p.value = cm.getColumnHeader(i) || "";
37472             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37473             if(!cm.isLocked(i)){
37474                 cb[cb.length] = ct.apply(p);
37475                 sb[sb.length] = st.apply(p);
37476             }else{
37477                 lb[lb.length] = ct.apply(p);
37478                 lsb[lsb.length] = st.apply(p);
37479             }
37480         }
37481         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37482                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37483     },
37484
37485     updateHeaders : function(){
37486         var html = this.renderHeaders();
37487         this.lockedHd.update(html[0]);
37488         this.mainHd.update(html[1]);
37489     },
37490
37491     /**
37492      * Focuses the specified row.
37493      * @param {Number} row The row index
37494      */
37495     focusRow : function(row)
37496     {
37497         //Roo.log('GridView.focusRow');
37498         var x = this.scroller.dom.scrollLeft;
37499         this.focusCell(row, 0, false);
37500         this.scroller.dom.scrollLeft = x;
37501     },
37502
37503     /**
37504      * Focuses the specified cell.
37505      * @param {Number} row The row index
37506      * @param {Number} col The column index
37507      * @param {Boolean} hscroll false to disable horizontal scrolling
37508      */
37509     focusCell : function(row, col, hscroll)
37510     {
37511         //Roo.log('GridView.focusCell');
37512         var el = this.ensureVisible(row, col, hscroll);
37513         this.focusEl.alignTo(el, "tl-tl");
37514         if(Roo.isGecko){
37515             this.focusEl.focus();
37516         }else{
37517             this.focusEl.focus.defer(1, this.focusEl);
37518         }
37519     },
37520
37521     /**
37522      * Scrolls the specified cell into view
37523      * @param {Number} row The row index
37524      * @param {Number} col The column index
37525      * @param {Boolean} hscroll false to disable horizontal scrolling
37526      */
37527     ensureVisible : function(row, col, hscroll)
37528     {
37529         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37530         //return null; //disable for testing.
37531         if(typeof row != "number"){
37532             row = row.rowIndex;
37533         }
37534         if(row < 0 && row >= this.ds.getCount()){
37535             return  null;
37536         }
37537         col = (col !== undefined ? col : 0);
37538         var cm = this.grid.colModel;
37539         while(cm.isHidden(col)){
37540             col++;
37541         }
37542
37543         var el = this.getCell(row, col);
37544         if(!el){
37545             return null;
37546         }
37547         var c = this.scroller.dom;
37548
37549         var ctop = parseInt(el.offsetTop, 10);
37550         var cleft = parseInt(el.offsetLeft, 10);
37551         var cbot = ctop + el.offsetHeight;
37552         var cright = cleft + el.offsetWidth;
37553         
37554         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37555         var stop = parseInt(c.scrollTop, 10);
37556         var sleft = parseInt(c.scrollLeft, 10);
37557         var sbot = stop + ch;
37558         var sright = sleft + c.clientWidth;
37559         /*
37560         Roo.log('GridView.ensureVisible:' +
37561                 ' ctop:' + ctop +
37562                 ' c.clientHeight:' + c.clientHeight +
37563                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37564                 ' stop:' + stop +
37565                 ' cbot:' + cbot +
37566                 ' sbot:' + sbot +
37567                 ' ch:' + ch  
37568                 );
37569         */
37570         if(ctop < stop){
37571              c.scrollTop = ctop;
37572             //Roo.log("set scrolltop to ctop DISABLE?");
37573         }else if(cbot > sbot){
37574             //Roo.log("set scrolltop to cbot-ch");
37575             c.scrollTop = cbot-ch;
37576         }
37577         
37578         if(hscroll !== false){
37579             if(cleft < sleft){
37580                 c.scrollLeft = cleft;
37581             }else if(cright > sright){
37582                 c.scrollLeft = cright-c.clientWidth;
37583             }
37584         }
37585          
37586         return el;
37587     },
37588
37589     updateColumns : function(){
37590         this.grid.stopEditing();
37591         var cm = this.grid.colModel, colIds = this.getColumnIds();
37592         //var totalWidth = cm.getTotalWidth();
37593         var pos = 0;
37594         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37595             //if(cm.isHidden(i)) continue;
37596             var w = cm.getColumnWidth(i);
37597             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37598             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37599         }
37600         this.updateSplitters();
37601     },
37602
37603     generateRules : function(cm){
37604         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37605         Roo.util.CSS.removeStyleSheet(rulesId);
37606         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37607             var cid = cm.getColumnId(i);
37608             var align = '';
37609             if(cm.config[i].align){
37610                 align = 'text-align:'+cm.config[i].align+';';
37611             }
37612             var hidden = '';
37613             if(cm.isHidden(i)){
37614                 hidden = 'display:none;';
37615             }
37616             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37617             ruleBuf.push(
37618                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37619                     this.hdSelector, cid, " {\n", align, width, "}\n",
37620                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37621                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37622         }
37623         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37624     },
37625
37626     updateSplitters : function(){
37627         var cm = this.cm, s = this.getSplitters();
37628         if(s){ // splitters not created yet
37629             var pos = 0, locked = true;
37630             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37631                 if(cm.isHidden(i)) {
37632                     continue;
37633                 }
37634                 var w = cm.getColumnWidth(i); // make sure it's a number
37635                 if(!cm.isLocked(i) && locked){
37636                     pos = 0;
37637                     locked = false;
37638                 }
37639                 pos += w;
37640                 s[i].style.left = (pos-this.splitOffset) + "px";
37641             }
37642         }
37643     },
37644
37645     handleHiddenChange : function(colModel, colIndex, hidden){
37646         if(hidden){
37647             this.hideColumn(colIndex);
37648         }else{
37649             this.unhideColumn(colIndex);
37650         }
37651     },
37652
37653     hideColumn : function(colIndex){
37654         var cid = this.getColumnId(colIndex);
37655         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37656         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37657         if(Roo.isSafari){
37658             this.updateHeaders();
37659         }
37660         this.updateSplitters();
37661         this.layout();
37662     },
37663
37664     unhideColumn : function(colIndex){
37665         var cid = this.getColumnId(colIndex);
37666         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37667         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37668
37669         if(Roo.isSafari){
37670             this.updateHeaders();
37671         }
37672         this.updateSplitters();
37673         this.layout();
37674     },
37675
37676     insertRows : function(dm, firstRow, lastRow, isUpdate){
37677         if(firstRow == 0 && lastRow == dm.getCount()-1){
37678             this.refresh();
37679         }else{
37680             if(!isUpdate){
37681                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37682             }
37683             var s = this.getScrollState();
37684             var markup = this.renderRows(firstRow, lastRow);
37685             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37686             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37687             this.restoreScroll(s);
37688             if(!isUpdate){
37689                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37690                 this.syncRowHeights(firstRow, lastRow);
37691                 this.stripeRows(firstRow);
37692                 this.layout();
37693             }
37694         }
37695     },
37696
37697     bufferRows : function(markup, target, index){
37698         var before = null, trows = target.rows, tbody = target.tBodies[0];
37699         if(index < trows.length){
37700             before = trows[index];
37701         }
37702         var b = document.createElement("div");
37703         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37704         var rows = b.firstChild.rows;
37705         for(var i = 0, len = rows.length; i < len; i++){
37706             if(before){
37707                 tbody.insertBefore(rows[0], before);
37708             }else{
37709                 tbody.appendChild(rows[0]);
37710             }
37711         }
37712         b.innerHTML = "";
37713         b = null;
37714     },
37715
37716     deleteRows : function(dm, firstRow, lastRow){
37717         if(dm.getRowCount()<1){
37718             this.fireEvent("beforerefresh", this);
37719             this.mainBody.update("");
37720             this.lockedBody.update("");
37721             this.fireEvent("refresh", this);
37722         }else{
37723             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37724             var bt = this.getBodyTable();
37725             var tbody = bt.firstChild;
37726             var rows = bt.rows;
37727             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37728                 tbody.removeChild(rows[firstRow]);
37729             }
37730             this.stripeRows(firstRow);
37731             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37732         }
37733     },
37734
37735     updateRows : function(dataSource, firstRow, lastRow){
37736         var s = this.getScrollState();
37737         this.refresh();
37738         this.restoreScroll(s);
37739     },
37740
37741     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37742         if(!noRefresh){
37743            this.refresh();
37744         }
37745         this.updateHeaderSortState();
37746     },
37747
37748     getScrollState : function(){
37749         
37750         var sb = this.scroller.dom;
37751         return {left: sb.scrollLeft, top: sb.scrollTop};
37752     },
37753
37754     stripeRows : function(startRow){
37755         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37756             return;
37757         }
37758         startRow = startRow || 0;
37759         var rows = this.getBodyTable().rows;
37760         var lrows = this.getLockedTable().rows;
37761         var cls = ' x-grid-row-alt ';
37762         for(var i = startRow, len = rows.length; i < len; i++){
37763             var row = rows[i], lrow = lrows[i];
37764             var isAlt = ((i+1) % 2 == 0);
37765             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37766             if(isAlt == hasAlt){
37767                 continue;
37768             }
37769             if(isAlt){
37770                 row.className += " x-grid-row-alt";
37771             }else{
37772                 row.className = row.className.replace("x-grid-row-alt", "");
37773             }
37774             if(lrow){
37775                 lrow.className = row.className;
37776             }
37777         }
37778     },
37779
37780     restoreScroll : function(state){
37781         //Roo.log('GridView.restoreScroll');
37782         var sb = this.scroller.dom;
37783         sb.scrollLeft = state.left;
37784         sb.scrollTop = state.top;
37785         this.syncScroll();
37786     },
37787
37788     syncScroll : function(){
37789         //Roo.log('GridView.syncScroll');
37790         var sb = this.scroller.dom;
37791         var sh = this.mainHd.dom;
37792         var bs = this.mainBody.dom;
37793         var lv = this.lockedBody.dom;
37794         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37795         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37796     },
37797
37798     handleScroll : function(e){
37799         this.syncScroll();
37800         var sb = this.scroller.dom;
37801         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37802         e.stopEvent();
37803     },
37804
37805     handleWheel : function(e){
37806         var d = e.getWheelDelta();
37807         this.scroller.dom.scrollTop -= d*22;
37808         // set this here to prevent jumpy scrolling on large tables
37809         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37810         e.stopEvent();
37811     },
37812
37813     renderRows : function(startRow, endRow){
37814         // pull in all the crap needed to render rows
37815         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37816         var colCount = cm.getColumnCount();
37817
37818         if(ds.getCount() < 1){
37819             return ["", ""];
37820         }
37821
37822         // build a map for all the columns
37823         var cs = [];
37824         for(var i = 0; i < colCount; i++){
37825             var name = cm.getDataIndex(i);
37826             cs[i] = {
37827                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37828                 renderer : cm.getRenderer(i),
37829                 id : cm.getColumnId(i),
37830                 locked : cm.isLocked(i),
37831                 has_editor : cm.editor ? true : false
37832             };
37833         }
37834
37835         startRow = startRow || 0;
37836         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37837
37838         // records to render
37839         var rs = ds.getRange(startRow, endRow);
37840
37841         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37842     },
37843
37844     // As much as I hate to duplicate code, this was branched because FireFox really hates
37845     // [].join("") on strings. The performance difference was substantial enough to
37846     // branch this function
37847     doRender : Roo.isGecko ?
37848             function(cs, rs, ds, startRow, colCount, stripe){
37849                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37850                 // buffers
37851                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37852                 
37853                 var hasListener = this.grid.hasListener('rowclass');
37854                 var rowcfg = {};
37855                 for(var j = 0, len = rs.length; j < len; j++){
37856                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37857                     for(var i = 0; i < colCount; i++){
37858                         c = cs[i];
37859                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37860                         p.id = c.id;
37861                         p.css = p.attr = "";
37862                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37863                         if(p.value == undefined || p.value === "") {
37864                             p.value = "&#160;";
37865                         }
37866                         if(c.has_editor){
37867                             Roo.log("adding editable celel css");
37868                             p.css += ' x-grid-editable-cell';
37869                         }
37870                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
37871                             p.css +=  ' x-grid-dirty-cell';
37872                         }
37873                         var markup = ct.apply(p);
37874                         if(!c.locked){
37875                             cb+= markup;
37876                         }else{
37877                             lcb+= markup;
37878                         }
37879                     }
37880                     var alt = [];
37881                     if(stripe && ((rowIndex+1) % 2 == 0)){
37882                         alt.push("x-grid-row-alt")
37883                     }
37884                     if(r.dirty){
37885                         alt.push(  " x-grid-dirty-row");
37886                     }
37887                     rp.cells = lcb;
37888                     if(this.getRowClass){
37889                         alt.push(this.getRowClass(r, rowIndex));
37890                     }
37891                     if (hasListener) {
37892                         rowcfg = {
37893                              
37894                             record: r,
37895                             rowIndex : rowIndex,
37896                             rowClass : ''
37897                         };
37898                         this.grid.fireEvent('rowclass', this, rowcfg);
37899                         alt.push(rowcfg.rowClass);
37900                     }
37901                     rp.alt = alt.join(" ");
37902                     lbuf+= rt.apply(rp);
37903                     rp.cells = cb;
37904                     buf+=  rt.apply(rp);
37905                 }
37906                 return [lbuf, buf];
37907             } :
37908             function(cs, rs, ds, startRow, colCount, stripe){
37909                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37910                 // buffers
37911                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37912                 var hasListener = this.grid.hasListener('rowclass');
37913  
37914                 var rowcfg = {};
37915                 for(var j = 0, len = rs.length; j < len; j++){
37916                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37917                     for(var i = 0; i < colCount; i++){
37918                         c = cs[i];
37919                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37920                         p.id = c.id;
37921                         p.css = p.attr = "";
37922                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37923                         if(p.value == undefined || p.value === "") {
37924                             p.value = "&#160;";
37925                         }
37926                         //Roo.log(c);
37927                          if(c.has_editor){
37928                             Roo.log("adding editable celel css");
37929                             p.css += ' x-grid-editable-cell';
37930                         }
37931                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37932                             p.css += ' x-grid-dirty-cell' 
37933                         }
37934                         
37935                         var markup = ct.apply(p);
37936                         if(!c.locked){
37937                             cb[cb.length] = markup;
37938                         }else{
37939                             lcb[lcb.length] = markup;
37940                         }
37941                     }
37942                     var alt = [];
37943                     if(stripe && ((rowIndex+1) % 2 == 0)){
37944                         alt.push( "x-grid-row-alt");
37945                     }
37946                     if(r.dirty){
37947                         alt.push(" x-grid-dirty-row");
37948                     }
37949                     rp.cells = lcb;
37950                     if(this.getRowClass){
37951                         alt.push( this.getRowClass(r, rowIndex));
37952                     }
37953                     if (hasListener) {
37954                         rowcfg = {
37955                              
37956                             record: r,
37957                             rowIndex : rowIndex,
37958                             rowClass : ''
37959                         };
37960                         this.grid.fireEvent('rowclass', this, rowcfg);
37961                         alt.push(rowcfg.rowClass);
37962                     }
37963                     Roo.log(alt);
37964                     rp.alt = alt.join(" ");
37965                     rp.cells = lcb.join("");
37966                     lbuf[lbuf.length] = rt.apply(rp);
37967                     rp.cells = cb.join("");
37968                     buf[buf.length] =  rt.apply(rp);
37969                 }
37970                 return [lbuf.join(""), buf.join("")];
37971             },
37972
37973     renderBody : function(){
37974         var markup = this.renderRows();
37975         var bt = this.templates.body;
37976         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37977     },
37978
37979     /**
37980      * Refreshes the grid
37981      * @param {Boolean} headersToo
37982      */
37983     refresh : function(headersToo){
37984         this.fireEvent("beforerefresh", this);
37985         this.grid.stopEditing();
37986         var result = this.renderBody();
37987         this.lockedBody.update(result[0]);
37988         this.mainBody.update(result[1]);
37989         if(headersToo === true){
37990             this.updateHeaders();
37991             this.updateColumns();
37992             this.updateSplitters();
37993             this.updateHeaderSortState();
37994         }
37995         this.syncRowHeights();
37996         this.layout();
37997         this.fireEvent("refresh", this);
37998     },
37999
38000     handleColumnMove : function(cm, oldIndex, newIndex){
38001         this.indexMap = null;
38002         var s = this.getScrollState();
38003         this.refresh(true);
38004         this.restoreScroll(s);
38005         this.afterMove(newIndex);
38006     },
38007
38008     afterMove : function(colIndex){
38009         if(this.enableMoveAnim && Roo.enableFx){
38010             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38011         }
38012         // if multisort - fix sortOrder, and reload..
38013         if (this.grid.dataSource.multiSort) {
38014             // the we can call sort again..
38015             var dm = this.grid.dataSource;
38016             var cm = this.grid.colModel;
38017             var so = [];
38018             for(var i = 0; i < cm.config.length; i++ ) {
38019                 
38020                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38021                     continue; // dont' bother, it's not in sort list or being set.
38022                 }
38023                 
38024                 so.push(cm.config[i].dataIndex);
38025             };
38026             dm.sortOrder = so;
38027             dm.load(dm.lastOptions);
38028             
38029             
38030         }
38031         
38032     },
38033
38034     updateCell : function(dm, rowIndex, dataIndex){
38035         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38036         if(typeof colIndex == "undefined"){ // not present in grid
38037             return;
38038         }
38039         var cm = this.grid.colModel;
38040         var cell = this.getCell(rowIndex, colIndex);
38041         var cellText = this.getCellText(rowIndex, colIndex);
38042
38043         var p = {
38044             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38045             id : cm.getColumnId(colIndex),
38046             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38047         };
38048         var renderer = cm.getRenderer(colIndex);
38049         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38050         if(typeof val == "undefined" || val === "") {
38051             val = "&#160;";
38052         }
38053         cellText.innerHTML = val;
38054         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38055         this.syncRowHeights(rowIndex, rowIndex);
38056     },
38057
38058     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38059         var maxWidth = 0;
38060         if(this.grid.autoSizeHeaders){
38061             var h = this.getHeaderCellMeasure(colIndex);
38062             maxWidth = Math.max(maxWidth, h.scrollWidth);
38063         }
38064         var tb, index;
38065         if(this.cm.isLocked(colIndex)){
38066             tb = this.getLockedTable();
38067             index = colIndex;
38068         }else{
38069             tb = this.getBodyTable();
38070             index = colIndex - this.cm.getLockedCount();
38071         }
38072         if(tb && tb.rows){
38073             var rows = tb.rows;
38074             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38075             for(var i = 0; i < stopIndex; i++){
38076                 var cell = rows[i].childNodes[index].firstChild;
38077                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38078             }
38079         }
38080         return maxWidth + /*margin for error in IE*/ 5;
38081     },
38082     /**
38083      * Autofit a column to its content.
38084      * @param {Number} colIndex
38085      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38086      */
38087      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38088          if(this.cm.isHidden(colIndex)){
38089              return; // can't calc a hidden column
38090          }
38091         if(forceMinSize){
38092             var cid = this.cm.getColumnId(colIndex);
38093             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38094            if(this.grid.autoSizeHeaders){
38095                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38096            }
38097         }
38098         var newWidth = this.calcColumnWidth(colIndex);
38099         this.cm.setColumnWidth(colIndex,
38100             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38101         if(!suppressEvent){
38102             this.grid.fireEvent("columnresize", colIndex, newWidth);
38103         }
38104     },
38105
38106     /**
38107      * Autofits all columns to their content and then expands to fit any extra space in the grid
38108      */
38109      autoSizeColumns : function(){
38110         var cm = this.grid.colModel;
38111         var colCount = cm.getColumnCount();
38112         for(var i = 0; i < colCount; i++){
38113             this.autoSizeColumn(i, true, true);
38114         }
38115         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38116             this.fitColumns();
38117         }else{
38118             this.updateColumns();
38119             this.layout();
38120         }
38121     },
38122
38123     /**
38124      * Autofits all columns to the grid's width proportionate with their current size
38125      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38126      */
38127     fitColumns : function(reserveScrollSpace){
38128         var cm = this.grid.colModel;
38129         var colCount = cm.getColumnCount();
38130         var cols = [];
38131         var width = 0;
38132         var i, w;
38133         for (i = 0; i < colCount; i++){
38134             if(!cm.isHidden(i) && !cm.isFixed(i)){
38135                 w = cm.getColumnWidth(i);
38136                 cols.push(i);
38137                 cols.push(w);
38138                 width += w;
38139             }
38140         }
38141         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38142         if(reserveScrollSpace){
38143             avail -= 17;
38144         }
38145         var frac = (avail - cm.getTotalWidth())/width;
38146         while (cols.length){
38147             w = cols.pop();
38148             i = cols.pop();
38149             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38150         }
38151         this.updateColumns();
38152         this.layout();
38153     },
38154
38155     onRowSelect : function(rowIndex){
38156         var row = this.getRowComposite(rowIndex);
38157         row.addClass("x-grid-row-selected");
38158     },
38159
38160     onRowDeselect : function(rowIndex){
38161         var row = this.getRowComposite(rowIndex);
38162         row.removeClass("x-grid-row-selected");
38163     },
38164
38165     onCellSelect : function(row, col){
38166         var cell = this.getCell(row, col);
38167         if(cell){
38168             Roo.fly(cell).addClass("x-grid-cell-selected");
38169         }
38170     },
38171
38172     onCellDeselect : function(row, col){
38173         var cell = this.getCell(row, col);
38174         if(cell){
38175             Roo.fly(cell).removeClass("x-grid-cell-selected");
38176         }
38177     },
38178
38179     updateHeaderSortState : function(){
38180         
38181         // sort state can be single { field: xxx, direction : yyy}
38182         // or   { xxx=>ASC , yyy : DESC ..... }
38183         
38184         var mstate = {};
38185         if (!this.ds.multiSort) { 
38186             var state = this.ds.getSortState();
38187             if(!state){
38188                 return;
38189             }
38190             mstate[state.field] = state.direction;
38191             // FIXME... - this is not used here.. but might be elsewhere..
38192             this.sortState = state;
38193             
38194         } else {
38195             mstate = this.ds.sortToggle;
38196         }
38197         //remove existing sort classes..
38198         
38199         var sc = this.sortClasses;
38200         var hds = this.el.select(this.headerSelector).removeClass(sc);
38201         
38202         for(var f in mstate) {
38203         
38204             var sortColumn = this.cm.findColumnIndex(f);
38205             
38206             if(sortColumn != -1){
38207                 var sortDir = mstate[f];        
38208                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38209             }
38210         }
38211         
38212          
38213         
38214     },
38215
38216
38217     handleHeaderClick : function(g, index,e){
38218         
38219         Roo.log("header click");
38220         
38221         if (Roo.isTouch) {
38222             // touch events on header are handled by context
38223             this.handleHdCtx(g,index,e);
38224             return;
38225         }
38226         
38227         
38228         if(this.headersDisabled){
38229             return;
38230         }
38231         var dm = g.dataSource, cm = g.colModel;
38232         if(!cm.isSortable(index)){
38233             return;
38234         }
38235         g.stopEditing();
38236         
38237         if (dm.multiSort) {
38238             // update the sortOrder
38239             var so = [];
38240             for(var i = 0; i < cm.config.length; i++ ) {
38241                 
38242                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38243                     continue; // dont' bother, it's not in sort list or being set.
38244                 }
38245                 
38246                 so.push(cm.config[i].dataIndex);
38247             };
38248             dm.sortOrder = so;
38249         }
38250         
38251         
38252         dm.sort(cm.getDataIndex(index));
38253     },
38254
38255
38256     destroy : function(){
38257         if(this.colMenu){
38258             this.colMenu.removeAll();
38259             Roo.menu.MenuMgr.unregister(this.colMenu);
38260             this.colMenu.getEl().remove();
38261             delete this.colMenu;
38262         }
38263         if(this.hmenu){
38264             this.hmenu.removeAll();
38265             Roo.menu.MenuMgr.unregister(this.hmenu);
38266             this.hmenu.getEl().remove();
38267             delete this.hmenu;
38268         }
38269         if(this.grid.enableColumnMove){
38270             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38271             if(dds){
38272                 for(var dd in dds){
38273                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38274                         var elid = dds[dd].dragElId;
38275                         dds[dd].unreg();
38276                         Roo.get(elid).remove();
38277                     } else if(dds[dd].config.isTarget){
38278                         dds[dd].proxyTop.remove();
38279                         dds[dd].proxyBottom.remove();
38280                         dds[dd].unreg();
38281                     }
38282                     if(Roo.dd.DDM.locationCache[dd]){
38283                         delete Roo.dd.DDM.locationCache[dd];
38284                     }
38285                 }
38286                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38287             }
38288         }
38289         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38290         this.bind(null, null);
38291         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38292     },
38293
38294     handleLockChange : function(){
38295         this.refresh(true);
38296     },
38297
38298     onDenyColumnLock : function(){
38299
38300     },
38301
38302     onDenyColumnHide : function(){
38303
38304     },
38305
38306     handleHdMenuClick : function(item){
38307         var index = this.hdCtxIndex;
38308         var cm = this.cm, ds = this.ds;
38309         switch(item.id){
38310             case "asc":
38311                 ds.sort(cm.getDataIndex(index), "ASC");
38312                 break;
38313             case "desc":
38314                 ds.sort(cm.getDataIndex(index), "DESC");
38315                 break;
38316             case "lock":
38317                 var lc = cm.getLockedCount();
38318                 if(cm.getColumnCount(true) <= lc+1){
38319                     this.onDenyColumnLock();
38320                     return;
38321                 }
38322                 if(lc != index){
38323                     cm.setLocked(index, true, true);
38324                     cm.moveColumn(index, lc);
38325                     this.grid.fireEvent("columnmove", index, lc);
38326                 }else{
38327                     cm.setLocked(index, true);
38328                 }
38329             break;
38330             case "unlock":
38331                 var lc = cm.getLockedCount();
38332                 if((lc-1) != index){
38333                     cm.setLocked(index, false, true);
38334                     cm.moveColumn(index, lc-1);
38335                     this.grid.fireEvent("columnmove", index, lc-1);
38336                 }else{
38337                     cm.setLocked(index, false);
38338                 }
38339             break;
38340             case 'wider': // used to expand cols on touch..
38341             case 'narrow':
38342                 var cw = cm.getColumnWidth(index);
38343                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38344                 cw = Math.max(0, cw);
38345                 cw = Math.min(cw,4000);
38346                 cm.setColumnWidth(index, cw);
38347                 break;
38348                 
38349             default:
38350                 index = cm.getIndexById(item.id.substr(4));
38351                 if(index != -1){
38352                     if(item.checked && cm.getColumnCount(true) <= 1){
38353                         this.onDenyColumnHide();
38354                         return false;
38355                     }
38356                     cm.setHidden(index, item.checked);
38357                 }
38358         }
38359         return true;
38360     },
38361
38362     beforeColMenuShow : function(){
38363         var cm = this.cm,  colCount = cm.getColumnCount();
38364         this.colMenu.removeAll();
38365         for(var i = 0; i < colCount; i++){
38366             this.colMenu.add(new Roo.menu.CheckItem({
38367                 id: "col-"+cm.getColumnId(i),
38368                 text: cm.getColumnHeader(i),
38369                 checked: !cm.isHidden(i),
38370                 hideOnClick:false
38371             }));
38372         }
38373     },
38374
38375     handleHdCtx : function(g, index, e){
38376         e.stopEvent();
38377         var hd = this.getHeaderCell(index);
38378         this.hdCtxIndex = index;
38379         var ms = this.hmenu.items, cm = this.cm;
38380         ms.get("asc").setDisabled(!cm.isSortable(index));
38381         ms.get("desc").setDisabled(!cm.isSortable(index));
38382         if(this.grid.enableColLock !== false){
38383             ms.get("lock").setDisabled(cm.isLocked(index));
38384             ms.get("unlock").setDisabled(!cm.isLocked(index));
38385         }
38386         this.hmenu.show(hd, "tl-bl");
38387     },
38388
38389     handleHdOver : function(e){
38390         var hd = this.findHeaderCell(e.getTarget());
38391         if(hd && !this.headersDisabled){
38392             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38393                this.fly(hd).addClass("x-grid-hd-over");
38394             }
38395         }
38396     },
38397
38398     handleHdOut : function(e){
38399         var hd = this.findHeaderCell(e.getTarget());
38400         if(hd){
38401             this.fly(hd).removeClass("x-grid-hd-over");
38402         }
38403     },
38404
38405     handleSplitDblClick : function(e, t){
38406         var i = this.getCellIndex(t);
38407         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38408             this.autoSizeColumn(i, true);
38409             this.layout();
38410         }
38411     },
38412
38413     render : function(){
38414
38415         var cm = this.cm;
38416         var colCount = cm.getColumnCount();
38417
38418         if(this.grid.monitorWindowResize === true){
38419             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38420         }
38421         var header = this.renderHeaders();
38422         var body = this.templates.body.apply({rows:""});
38423         var html = this.templates.master.apply({
38424             lockedBody: body,
38425             body: body,
38426             lockedHeader: header[0],
38427             header: header[1]
38428         });
38429
38430         //this.updateColumns();
38431
38432         this.grid.getGridEl().dom.innerHTML = html;
38433
38434         this.initElements();
38435         
38436         // a kludge to fix the random scolling effect in webkit
38437         this.el.on("scroll", function() {
38438             this.el.dom.scrollTop=0; // hopefully not recursive..
38439         },this);
38440
38441         this.scroller.on("scroll", this.handleScroll, this);
38442         this.lockedBody.on("mousewheel", this.handleWheel, this);
38443         this.mainBody.on("mousewheel", this.handleWheel, this);
38444
38445         this.mainHd.on("mouseover", this.handleHdOver, this);
38446         this.mainHd.on("mouseout", this.handleHdOut, this);
38447         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38448                 {delegate: "."+this.splitClass});
38449
38450         this.lockedHd.on("mouseover", this.handleHdOver, this);
38451         this.lockedHd.on("mouseout", this.handleHdOut, this);
38452         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38453                 {delegate: "."+this.splitClass});
38454
38455         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38456             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38457         }
38458
38459         this.updateSplitters();
38460
38461         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38462             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38463             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38464         }
38465
38466         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38467             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38468             this.hmenu.add(
38469                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38470                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38471             );
38472             if(this.grid.enableColLock !== false){
38473                 this.hmenu.add('-',
38474                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38475                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38476                 );
38477             }
38478             if (Roo.isTouch) {
38479                  this.hmenu.add('-',
38480                     {id:"wider", text: this.columnsWiderText},
38481                     {id:"narrow", text: this.columnsNarrowText }
38482                 );
38483                 
38484                  
38485             }
38486             
38487             if(this.grid.enableColumnHide !== false){
38488
38489                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38490                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38491                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38492
38493                 this.hmenu.add('-',
38494                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38495                 );
38496             }
38497             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38498
38499             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38500         }
38501
38502         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38503             this.dd = new Roo.grid.GridDragZone(this.grid, {
38504                 ddGroup : this.grid.ddGroup || 'GridDD'
38505             });
38506             
38507         }
38508
38509         /*
38510         for(var i = 0; i < colCount; i++){
38511             if(cm.isHidden(i)){
38512                 this.hideColumn(i);
38513             }
38514             if(cm.config[i].align){
38515                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38516                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38517             }
38518         }*/
38519         
38520         this.updateHeaderSortState();
38521
38522         this.beforeInitialResize();
38523         this.layout(true);
38524
38525         // two part rendering gives faster view to the user
38526         this.renderPhase2.defer(1, this);
38527     },
38528
38529     renderPhase2 : function(){
38530         // render the rows now
38531         this.refresh();
38532         if(this.grid.autoSizeColumns){
38533             this.autoSizeColumns();
38534         }
38535     },
38536
38537     beforeInitialResize : function(){
38538
38539     },
38540
38541     onColumnSplitterMoved : function(i, w){
38542         this.userResized = true;
38543         var cm = this.grid.colModel;
38544         cm.setColumnWidth(i, w, true);
38545         var cid = cm.getColumnId(i);
38546         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38547         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38548         this.updateSplitters();
38549         this.layout();
38550         this.grid.fireEvent("columnresize", i, w);
38551     },
38552
38553     syncRowHeights : function(startIndex, endIndex){
38554         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38555             startIndex = startIndex || 0;
38556             var mrows = this.getBodyTable().rows;
38557             var lrows = this.getLockedTable().rows;
38558             var len = mrows.length-1;
38559             endIndex = Math.min(endIndex || len, len);
38560             for(var i = startIndex; i <= endIndex; i++){
38561                 var m = mrows[i], l = lrows[i];
38562                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38563                 m.style.height = l.style.height = h + "px";
38564             }
38565         }
38566     },
38567
38568     layout : function(initialRender, is2ndPass){
38569         var g = this.grid;
38570         var auto = g.autoHeight;
38571         var scrollOffset = 16;
38572         var c = g.getGridEl(), cm = this.cm,
38573                 expandCol = g.autoExpandColumn,
38574                 gv = this;
38575         //c.beginMeasure();
38576
38577         if(!c.dom.offsetWidth){ // display:none?
38578             if(initialRender){
38579                 this.lockedWrap.show();
38580                 this.mainWrap.show();
38581             }
38582             return;
38583         }
38584
38585         var hasLock = this.cm.isLocked(0);
38586
38587         var tbh = this.headerPanel.getHeight();
38588         var bbh = this.footerPanel.getHeight();
38589
38590         if(auto){
38591             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38592             var newHeight = ch + c.getBorderWidth("tb");
38593             if(g.maxHeight){
38594                 newHeight = Math.min(g.maxHeight, newHeight);
38595             }
38596             c.setHeight(newHeight);
38597         }
38598
38599         if(g.autoWidth){
38600             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38601         }
38602
38603         var s = this.scroller;
38604
38605         var csize = c.getSize(true);
38606
38607         this.el.setSize(csize.width, csize.height);
38608
38609         this.headerPanel.setWidth(csize.width);
38610         this.footerPanel.setWidth(csize.width);
38611
38612         var hdHeight = this.mainHd.getHeight();
38613         var vw = csize.width;
38614         var vh = csize.height - (tbh + bbh);
38615
38616         s.setSize(vw, vh);
38617
38618         var bt = this.getBodyTable();
38619         var ltWidth = hasLock ?
38620                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38621
38622         var scrollHeight = bt.offsetHeight;
38623         var scrollWidth = ltWidth + bt.offsetWidth;
38624         var vscroll = false, hscroll = false;
38625
38626         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38627
38628         var lw = this.lockedWrap, mw = this.mainWrap;
38629         var lb = this.lockedBody, mb = this.mainBody;
38630
38631         setTimeout(function(){
38632             var t = s.dom.offsetTop;
38633             var w = s.dom.clientWidth,
38634                 h = s.dom.clientHeight;
38635
38636             lw.setTop(t);
38637             lw.setSize(ltWidth, h);
38638
38639             mw.setLeftTop(ltWidth, t);
38640             mw.setSize(w-ltWidth, h);
38641
38642             lb.setHeight(h-hdHeight);
38643             mb.setHeight(h-hdHeight);
38644
38645             if(is2ndPass !== true && !gv.userResized && expandCol){
38646                 // high speed resize without full column calculation
38647                 
38648                 var ci = cm.getIndexById(expandCol);
38649                 if (ci < 0) {
38650                     ci = cm.findColumnIndex(expandCol);
38651                 }
38652                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38653                 var expandId = cm.getColumnId(ci);
38654                 var  tw = cm.getTotalWidth(false);
38655                 var currentWidth = cm.getColumnWidth(ci);
38656                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38657                 if(currentWidth != cw){
38658                     cm.setColumnWidth(ci, cw, true);
38659                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38660                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38661                     gv.updateSplitters();
38662                     gv.layout(false, true);
38663                 }
38664             }
38665
38666             if(initialRender){
38667                 lw.show();
38668                 mw.show();
38669             }
38670             //c.endMeasure();
38671         }, 10);
38672     },
38673
38674     onWindowResize : function(){
38675         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38676             return;
38677         }
38678         this.layout();
38679     },
38680
38681     appendFooter : function(parentEl){
38682         return null;
38683     },
38684
38685     sortAscText : "Sort Ascending",
38686     sortDescText : "Sort Descending",
38687     lockText : "Lock Column",
38688     unlockText : "Unlock Column",
38689     columnsText : "Columns",
38690  
38691     columnsWiderText : "Wider",
38692     columnsNarrowText : "Thinner"
38693 });
38694
38695
38696 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38697     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38698     this.proxy.el.addClass('x-grid3-col-dd');
38699 };
38700
38701 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38702     handleMouseDown : function(e){
38703
38704     },
38705
38706     callHandleMouseDown : function(e){
38707         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38708     }
38709 });
38710 /*
38711  * Based on:
38712  * Ext JS Library 1.1.1
38713  * Copyright(c) 2006-2007, Ext JS, LLC.
38714  *
38715  * Originally Released Under LGPL - original licence link has changed is not relivant.
38716  *
38717  * Fork - LGPL
38718  * <script type="text/javascript">
38719  */
38720  
38721 // private
38722 // This is a support class used internally by the Grid components
38723 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38724     this.grid = grid;
38725     this.view = grid.getView();
38726     this.proxy = this.view.resizeProxy;
38727     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38728         "gridSplitters" + this.grid.getGridEl().id, {
38729         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38730     });
38731     this.setHandleElId(Roo.id(hd));
38732     this.setOuterHandleElId(Roo.id(hd2));
38733     this.scroll = false;
38734 };
38735 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38736     fly: Roo.Element.fly,
38737
38738     b4StartDrag : function(x, y){
38739         this.view.headersDisabled = true;
38740         this.proxy.setHeight(this.view.mainWrap.getHeight());
38741         var w = this.cm.getColumnWidth(this.cellIndex);
38742         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38743         this.resetConstraints();
38744         this.setXConstraint(minw, 1000);
38745         this.setYConstraint(0, 0);
38746         this.minX = x - minw;
38747         this.maxX = x + 1000;
38748         this.startPos = x;
38749         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38750     },
38751
38752
38753     handleMouseDown : function(e){
38754         ev = Roo.EventObject.setEvent(e);
38755         var t = this.fly(ev.getTarget());
38756         if(t.hasClass("x-grid-split")){
38757             this.cellIndex = this.view.getCellIndex(t.dom);
38758             this.split = t.dom;
38759             this.cm = this.grid.colModel;
38760             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38761                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38762             }
38763         }
38764     },
38765
38766     endDrag : function(e){
38767         this.view.headersDisabled = false;
38768         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38769         var diff = endX - this.startPos;
38770         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38771     },
38772
38773     autoOffset : function(){
38774         this.setDelta(0,0);
38775     }
38776 });/*
38777  * Based on:
38778  * Ext JS Library 1.1.1
38779  * Copyright(c) 2006-2007, Ext JS, LLC.
38780  *
38781  * Originally Released Under LGPL - original licence link has changed is not relivant.
38782  *
38783  * Fork - LGPL
38784  * <script type="text/javascript">
38785  */
38786  
38787 // private
38788 // This is a support class used internally by the Grid components
38789 Roo.grid.GridDragZone = function(grid, config){
38790     this.view = grid.getView();
38791     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38792     if(this.view.lockedBody){
38793         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38794         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38795     }
38796     this.scroll = false;
38797     this.grid = grid;
38798     this.ddel = document.createElement('div');
38799     this.ddel.className = 'x-grid-dd-wrap';
38800 };
38801
38802 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38803     ddGroup : "GridDD",
38804
38805     getDragData : function(e){
38806         var t = Roo.lib.Event.getTarget(e);
38807         var rowIndex = this.view.findRowIndex(t);
38808         var sm = this.grid.selModel;
38809             
38810         //Roo.log(rowIndex);
38811         
38812         if (sm.getSelectedCell) {
38813             // cell selection..
38814             if (!sm.getSelectedCell()) {
38815                 return false;
38816             }
38817             if (rowIndex != sm.getSelectedCell()[0]) {
38818                 return false;
38819             }
38820         
38821         }
38822         
38823         if(rowIndex !== false){
38824             
38825             // if editorgrid.. 
38826             
38827             
38828             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38829                
38830             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38831               //  
38832             //}
38833             if (e.hasModifier()){
38834                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38835             }
38836             
38837             Roo.log("getDragData");
38838             
38839             return {
38840                 grid: this.grid,
38841                 ddel: this.ddel,
38842                 rowIndex: rowIndex,
38843                 selections:sm.getSelections ? sm.getSelections() : (
38844                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38845                 )
38846             };
38847         }
38848         return false;
38849     },
38850
38851     onInitDrag : function(e){
38852         var data = this.dragData;
38853         this.ddel.innerHTML = this.grid.getDragDropText();
38854         this.proxy.update(this.ddel);
38855         // fire start drag?
38856     },
38857
38858     afterRepair : function(){
38859         this.dragging = false;
38860     },
38861
38862     getRepairXY : function(e, data){
38863         return false;
38864     },
38865
38866     onEndDrag : function(data, e){
38867         // fire end drag?
38868     },
38869
38870     onValidDrop : function(dd, e, id){
38871         // fire drag drop?
38872         this.hideProxy();
38873     },
38874
38875     beforeInvalidDrop : function(e, id){
38876
38877     }
38878 });/*
38879  * Based on:
38880  * Ext JS Library 1.1.1
38881  * Copyright(c) 2006-2007, Ext JS, LLC.
38882  *
38883  * Originally Released Under LGPL - original licence link has changed is not relivant.
38884  *
38885  * Fork - LGPL
38886  * <script type="text/javascript">
38887  */
38888  
38889
38890 /**
38891  * @class Roo.grid.ColumnModel
38892  * @extends Roo.util.Observable
38893  * This is the default implementation of a ColumnModel used by the Grid. It defines
38894  * the columns in the grid.
38895  * <br>Usage:<br>
38896  <pre><code>
38897  var colModel = new Roo.grid.ColumnModel([
38898         {header: "Ticker", width: 60, sortable: true, locked: true},
38899         {header: "Company Name", width: 150, sortable: true},
38900         {header: "Market Cap.", width: 100, sortable: true},
38901         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38902         {header: "Employees", width: 100, sortable: true, resizable: false}
38903  ]);
38904  </code></pre>
38905  * <p>
38906  
38907  * The config options listed for this class are options which may appear in each
38908  * individual column definition.
38909  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38910  * @constructor
38911  * @param {Object} config An Array of column config objects. See this class's
38912  * config objects for details.
38913 */
38914 Roo.grid.ColumnModel = function(config){
38915         /**
38916      * The config passed into the constructor
38917      */
38918     this.config = config;
38919     this.lookup = {};
38920
38921     // if no id, create one
38922     // if the column does not have a dataIndex mapping,
38923     // map it to the order it is in the config
38924     for(var i = 0, len = config.length; i < len; i++){
38925         var c = config[i];
38926         if(typeof c.dataIndex == "undefined"){
38927             c.dataIndex = i;
38928         }
38929         if(typeof c.renderer == "string"){
38930             c.renderer = Roo.util.Format[c.renderer];
38931         }
38932         if(typeof c.id == "undefined"){
38933             c.id = Roo.id();
38934         }
38935         if(c.editor && c.editor.xtype){
38936             c.editor  = Roo.factory(c.editor, Roo.grid);
38937         }
38938         if(c.editor && c.editor.isFormField){
38939             c.editor = new Roo.grid.GridEditor(c.editor);
38940         }
38941         this.lookup[c.id] = c;
38942     }
38943
38944     /**
38945      * The width of columns which have no width specified (defaults to 100)
38946      * @type Number
38947      */
38948     this.defaultWidth = 100;
38949
38950     /**
38951      * Default sortable of columns which have no sortable specified (defaults to false)
38952      * @type Boolean
38953      */
38954     this.defaultSortable = false;
38955
38956     this.addEvents({
38957         /**
38958              * @event widthchange
38959              * Fires when the width of a column changes.
38960              * @param {ColumnModel} this
38961              * @param {Number} columnIndex The column index
38962              * @param {Number} newWidth The new width
38963              */
38964             "widthchange": true,
38965         /**
38966              * @event headerchange
38967              * Fires when the text of a header changes.
38968              * @param {ColumnModel} this
38969              * @param {Number} columnIndex The column index
38970              * @param {Number} newText The new header text
38971              */
38972             "headerchange": true,
38973         /**
38974              * @event hiddenchange
38975              * Fires when a column is hidden or "unhidden".
38976              * @param {ColumnModel} this
38977              * @param {Number} columnIndex The column index
38978              * @param {Boolean} hidden true if hidden, false otherwise
38979              */
38980             "hiddenchange": true,
38981             /**
38982          * @event columnmoved
38983          * Fires when a column is moved.
38984          * @param {ColumnModel} this
38985          * @param {Number} oldIndex
38986          * @param {Number} newIndex
38987          */
38988         "columnmoved" : true,
38989         /**
38990          * @event columlockchange
38991          * Fires when a column's locked state is changed
38992          * @param {ColumnModel} this
38993          * @param {Number} colIndex
38994          * @param {Boolean} locked true if locked
38995          */
38996         "columnlockchange" : true
38997     });
38998     Roo.grid.ColumnModel.superclass.constructor.call(this);
38999 };
39000 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39001     /**
39002      * @cfg {String} header The header text to display in the Grid view.
39003      */
39004     /**
39005      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39006      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39007      * specified, the column's index is used as an index into the Record's data Array.
39008      */
39009     /**
39010      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39011      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39012      */
39013     /**
39014      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39015      * Defaults to the value of the {@link #defaultSortable} property.
39016      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39017      */
39018     /**
39019      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39020      */
39021     /**
39022      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39023      */
39024     /**
39025      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39026      */
39027     /**
39028      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39029      */
39030     /**
39031      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39032      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39033      * default renderer uses the raw data value. If an object is returned (bootstrap only)
39034      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39035      */
39036        /**
39037      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39038      */
39039     /**
39040      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39041      */
39042     /**
39043      * @cfg {String} cursor (Optional)
39044      */
39045     /**
39046      * @cfg {String} tooltip (Optional)
39047      */
39048     /**
39049      * @cfg {Number} xs (Optional)
39050      */
39051     /**
39052      * @cfg {Number} sm (Optional)
39053      */
39054     /**
39055      * @cfg {Number} md (Optional)
39056      */
39057     /**
39058      * @cfg {Number} lg (Optional)
39059      */
39060     /**
39061      * Returns the id of the column at the specified index.
39062      * @param {Number} index The column index
39063      * @return {String} the id
39064      */
39065     getColumnId : function(index){
39066         return this.config[index].id;
39067     },
39068
39069     /**
39070      * Returns the column for a specified id.
39071      * @param {String} id The column id
39072      * @return {Object} the column
39073      */
39074     getColumnById : function(id){
39075         return this.lookup[id];
39076     },
39077
39078     
39079     /**
39080      * Returns the column for a specified dataIndex.
39081      * @param {String} dataIndex The column dataIndex
39082      * @return {Object|Boolean} the column or false if not found
39083      */
39084     getColumnByDataIndex: function(dataIndex){
39085         var index = this.findColumnIndex(dataIndex);
39086         return index > -1 ? this.config[index] : false;
39087     },
39088     
39089     /**
39090      * Returns the index for a specified column id.
39091      * @param {String} id The column id
39092      * @return {Number} the index, or -1 if not found
39093      */
39094     getIndexById : function(id){
39095         for(var i = 0, len = this.config.length; i < len; i++){
39096             if(this.config[i].id == id){
39097                 return i;
39098             }
39099         }
39100         return -1;
39101     },
39102     
39103     /**
39104      * Returns the index for a specified column dataIndex.
39105      * @param {String} dataIndex The column dataIndex
39106      * @return {Number} the index, or -1 if not found
39107      */
39108     
39109     findColumnIndex : function(dataIndex){
39110         for(var i = 0, len = this.config.length; i < len; i++){
39111             if(this.config[i].dataIndex == dataIndex){
39112                 return i;
39113             }
39114         }
39115         return -1;
39116     },
39117     
39118     
39119     moveColumn : function(oldIndex, newIndex){
39120         var c = this.config[oldIndex];
39121         this.config.splice(oldIndex, 1);
39122         this.config.splice(newIndex, 0, c);
39123         this.dataMap = null;
39124         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39125     },
39126
39127     isLocked : function(colIndex){
39128         return this.config[colIndex].locked === true;
39129     },
39130
39131     setLocked : function(colIndex, value, suppressEvent){
39132         if(this.isLocked(colIndex) == value){
39133             return;
39134         }
39135         this.config[colIndex].locked = value;
39136         if(!suppressEvent){
39137             this.fireEvent("columnlockchange", this, colIndex, value);
39138         }
39139     },
39140
39141     getTotalLockedWidth : function(){
39142         var totalWidth = 0;
39143         for(var i = 0; i < this.config.length; i++){
39144             if(this.isLocked(i) && !this.isHidden(i)){
39145                 this.totalWidth += this.getColumnWidth(i);
39146             }
39147         }
39148         return totalWidth;
39149     },
39150
39151     getLockedCount : function(){
39152         for(var i = 0, len = this.config.length; i < len; i++){
39153             if(!this.isLocked(i)){
39154                 return i;
39155             }
39156         }
39157     },
39158
39159     /**
39160      * Returns the number of columns.
39161      * @return {Number}
39162      */
39163     getColumnCount : function(visibleOnly){
39164         if(visibleOnly === true){
39165             var c = 0;
39166             for(var i = 0, len = this.config.length; i < len; i++){
39167                 if(!this.isHidden(i)){
39168                     c++;
39169                 }
39170             }
39171             return c;
39172         }
39173         return this.config.length;
39174     },
39175
39176     /**
39177      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39178      * @param {Function} fn
39179      * @param {Object} scope (optional)
39180      * @return {Array} result
39181      */
39182     getColumnsBy : function(fn, scope){
39183         var r = [];
39184         for(var i = 0, len = this.config.length; i < len; i++){
39185             var c = this.config[i];
39186             if(fn.call(scope||this, c, i) === true){
39187                 r[r.length] = c;
39188             }
39189         }
39190         return r;
39191     },
39192
39193     /**
39194      * Returns true if the specified column is sortable.
39195      * @param {Number} col The column index
39196      * @return {Boolean}
39197      */
39198     isSortable : function(col){
39199         if(typeof this.config[col].sortable == "undefined"){
39200             return this.defaultSortable;
39201         }
39202         return this.config[col].sortable;
39203     },
39204
39205     /**
39206      * Returns the rendering (formatting) function defined for the column.
39207      * @param {Number} col The column index.
39208      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39209      */
39210     getRenderer : function(col){
39211         if(!this.config[col].renderer){
39212             return Roo.grid.ColumnModel.defaultRenderer;
39213         }
39214         return this.config[col].renderer;
39215     },
39216
39217     /**
39218      * Sets the rendering (formatting) function for a column.
39219      * @param {Number} col The column index
39220      * @param {Function} fn The function to use to process the cell's raw data
39221      * to return HTML markup for the grid view. The render function is called with
39222      * the following parameters:<ul>
39223      * <li>Data value.</li>
39224      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39225      * <li>css A CSS style string to apply to the table cell.</li>
39226      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39227      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39228      * <li>Row index</li>
39229      * <li>Column index</li>
39230      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39231      */
39232     setRenderer : function(col, fn){
39233         this.config[col].renderer = fn;
39234     },
39235
39236     /**
39237      * Returns the width for the specified column.
39238      * @param {Number} col The column index
39239      * @return {Number}
39240      */
39241     getColumnWidth : function(col){
39242         return this.config[col].width * 1 || this.defaultWidth;
39243     },
39244
39245     /**
39246      * Sets the width for a column.
39247      * @param {Number} col The column index
39248      * @param {Number} width The new width
39249      */
39250     setColumnWidth : function(col, width, suppressEvent){
39251         this.config[col].width = width;
39252         this.totalWidth = null;
39253         if(!suppressEvent){
39254              this.fireEvent("widthchange", this, col, width);
39255         }
39256     },
39257
39258     /**
39259      * Returns the total width of all columns.
39260      * @param {Boolean} includeHidden True to include hidden column widths
39261      * @return {Number}
39262      */
39263     getTotalWidth : function(includeHidden){
39264         if(!this.totalWidth){
39265             this.totalWidth = 0;
39266             for(var i = 0, len = this.config.length; i < len; i++){
39267                 if(includeHidden || !this.isHidden(i)){
39268                     this.totalWidth += this.getColumnWidth(i);
39269                 }
39270             }
39271         }
39272         return this.totalWidth;
39273     },
39274
39275     /**
39276      * Returns the header for the specified column.
39277      * @param {Number} col The column index
39278      * @return {String}
39279      */
39280     getColumnHeader : function(col){
39281         return this.config[col].header;
39282     },
39283
39284     /**
39285      * Sets the header for a column.
39286      * @param {Number} col The column index
39287      * @param {String} header The new header
39288      */
39289     setColumnHeader : function(col, header){
39290         this.config[col].header = header;
39291         this.fireEvent("headerchange", this, col, header);
39292     },
39293
39294     /**
39295      * Returns the tooltip for the specified column.
39296      * @param {Number} col The column index
39297      * @return {String}
39298      */
39299     getColumnTooltip : function(col){
39300             return this.config[col].tooltip;
39301     },
39302     /**
39303      * Sets the tooltip for a column.
39304      * @param {Number} col The column index
39305      * @param {String} tooltip The new tooltip
39306      */
39307     setColumnTooltip : function(col, tooltip){
39308             this.config[col].tooltip = tooltip;
39309     },
39310
39311     /**
39312      * Returns the dataIndex for the specified column.
39313      * @param {Number} col The column index
39314      * @return {Number}
39315      */
39316     getDataIndex : function(col){
39317         return this.config[col].dataIndex;
39318     },
39319
39320     /**
39321      * Sets the dataIndex for a column.
39322      * @param {Number} col The column index
39323      * @param {Number} dataIndex The new dataIndex
39324      */
39325     setDataIndex : function(col, dataIndex){
39326         this.config[col].dataIndex = dataIndex;
39327     },
39328
39329     
39330     
39331     /**
39332      * Returns true if the cell is editable.
39333      * @param {Number} colIndex The column index
39334      * @param {Number} rowIndex The row index
39335      * @return {Boolean}
39336      */
39337     isCellEditable : function(colIndex, rowIndex){
39338         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39339     },
39340
39341     /**
39342      * Returns the editor defined for the cell/column.
39343      * return false or null to disable editing.
39344      * @param {Number} colIndex The column index
39345      * @param {Number} rowIndex The row index
39346      * @return {Object}
39347      */
39348     getCellEditor : function(colIndex, rowIndex){
39349         return this.config[colIndex].editor;
39350     },
39351
39352     /**
39353      * Sets if a column is editable.
39354      * @param {Number} col The column index
39355      * @param {Boolean} editable True if the column is editable
39356      */
39357     setEditable : function(col, editable){
39358         this.config[col].editable = editable;
39359     },
39360
39361
39362     /**
39363      * Returns true if the column is hidden.
39364      * @param {Number} colIndex The column index
39365      * @return {Boolean}
39366      */
39367     isHidden : function(colIndex){
39368         return this.config[colIndex].hidden;
39369     },
39370
39371
39372     /**
39373      * Returns true if the column width cannot be changed
39374      */
39375     isFixed : function(colIndex){
39376         return this.config[colIndex].fixed;
39377     },
39378
39379     /**
39380      * Returns true if the column can be resized
39381      * @return {Boolean}
39382      */
39383     isResizable : function(colIndex){
39384         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39385     },
39386     /**
39387      * Sets if a column is hidden.
39388      * @param {Number} colIndex The column index
39389      * @param {Boolean} hidden True if the column is hidden
39390      */
39391     setHidden : function(colIndex, hidden){
39392         this.config[colIndex].hidden = hidden;
39393         this.totalWidth = null;
39394         this.fireEvent("hiddenchange", this, colIndex, hidden);
39395     },
39396
39397     /**
39398      * Sets the editor for a column.
39399      * @param {Number} col The column index
39400      * @param {Object} editor The editor object
39401      */
39402     setEditor : function(col, editor){
39403         this.config[col].editor = editor;
39404     }
39405 });
39406
39407 Roo.grid.ColumnModel.defaultRenderer = function(value){
39408         if(typeof value == "string" && value.length < 1){
39409             return "&#160;";
39410         }
39411         return value;
39412 };
39413
39414 // Alias for backwards compatibility
39415 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39416 /*
39417  * Based on:
39418  * Ext JS Library 1.1.1
39419  * Copyright(c) 2006-2007, Ext JS, LLC.
39420  *
39421  * Originally Released Under LGPL - original licence link has changed is not relivant.
39422  *
39423  * Fork - LGPL
39424  * <script type="text/javascript">
39425  */
39426
39427 /**
39428  * @class Roo.grid.AbstractSelectionModel
39429  * @extends Roo.util.Observable
39430  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39431  * implemented by descendant classes.  This class should not be directly instantiated.
39432  * @constructor
39433  */
39434 Roo.grid.AbstractSelectionModel = function(){
39435     this.locked = false;
39436     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39437 };
39438
39439 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39440     /** @ignore Called by the grid automatically. Do not call directly. */
39441     init : function(grid){
39442         this.grid = grid;
39443         this.initEvents();
39444     },
39445
39446     /**
39447      * Locks the selections.
39448      */
39449     lock : function(){
39450         this.locked = true;
39451     },
39452
39453     /**
39454      * Unlocks the selections.
39455      */
39456     unlock : function(){
39457         this.locked = false;
39458     },
39459
39460     /**
39461      * Returns true if the selections are locked.
39462      * @return {Boolean}
39463      */
39464     isLocked : function(){
39465         return this.locked;
39466     }
39467 });/*
39468  * Based on:
39469  * Ext JS Library 1.1.1
39470  * Copyright(c) 2006-2007, Ext JS, LLC.
39471  *
39472  * Originally Released Under LGPL - original licence link has changed is not relivant.
39473  *
39474  * Fork - LGPL
39475  * <script type="text/javascript">
39476  */
39477 /**
39478  * @extends Roo.grid.AbstractSelectionModel
39479  * @class Roo.grid.RowSelectionModel
39480  * The default SelectionModel used by {@link Roo.grid.Grid}.
39481  * It supports multiple selections and keyboard selection/navigation. 
39482  * @constructor
39483  * @param {Object} config
39484  */
39485 Roo.grid.RowSelectionModel = function(config){
39486     Roo.apply(this, config);
39487     this.selections = new Roo.util.MixedCollection(false, function(o){
39488         return o.id;
39489     });
39490
39491     this.last = false;
39492     this.lastActive = false;
39493
39494     this.addEvents({
39495         /**
39496              * @event selectionchange
39497              * Fires when the selection changes
39498              * @param {SelectionModel} this
39499              */
39500             "selectionchange" : true,
39501         /**
39502              * @event afterselectionchange
39503              * Fires after the selection changes (eg. by key press or clicking)
39504              * @param {SelectionModel} this
39505              */
39506             "afterselectionchange" : true,
39507         /**
39508              * @event beforerowselect
39509              * Fires when a row is selected being selected, return false to cancel.
39510              * @param {SelectionModel} this
39511              * @param {Number} rowIndex The selected index
39512              * @param {Boolean} keepExisting False if other selections will be cleared
39513              */
39514             "beforerowselect" : true,
39515         /**
39516              * @event rowselect
39517              * Fires when a row is selected.
39518              * @param {SelectionModel} this
39519              * @param {Number} rowIndex The selected index
39520              * @param {Roo.data.Record} r The record
39521              */
39522             "rowselect" : true,
39523         /**
39524              * @event rowdeselect
39525              * Fires when a row is deselected.
39526              * @param {SelectionModel} this
39527              * @param {Number} rowIndex The selected index
39528              */
39529         "rowdeselect" : true
39530     });
39531     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39532     this.locked = false;
39533 };
39534
39535 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39536     /**
39537      * @cfg {Boolean} singleSelect
39538      * True to allow selection of only one row at a time (defaults to false)
39539      */
39540     singleSelect : false,
39541
39542     // private
39543     initEvents : function(){
39544
39545         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39546             this.grid.on("mousedown", this.handleMouseDown, this);
39547         }else{ // allow click to work like normal
39548             this.grid.on("rowclick", this.handleDragableRowClick, this);
39549         }
39550
39551         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39552             "up" : function(e){
39553                 if(!e.shiftKey){
39554                     this.selectPrevious(e.shiftKey);
39555                 }else if(this.last !== false && this.lastActive !== false){
39556                     var last = this.last;
39557                     this.selectRange(this.last,  this.lastActive-1);
39558                     this.grid.getView().focusRow(this.lastActive);
39559                     if(last !== false){
39560                         this.last = last;
39561                     }
39562                 }else{
39563                     this.selectFirstRow();
39564                 }
39565                 this.fireEvent("afterselectionchange", this);
39566             },
39567             "down" : function(e){
39568                 if(!e.shiftKey){
39569                     this.selectNext(e.shiftKey);
39570                 }else if(this.last !== false && this.lastActive !== false){
39571                     var last = this.last;
39572                     this.selectRange(this.last,  this.lastActive+1);
39573                     this.grid.getView().focusRow(this.lastActive);
39574                     if(last !== false){
39575                         this.last = last;
39576                     }
39577                 }else{
39578                     this.selectFirstRow();
39579                 }
39580                 this.fireEvent("afterselectionchange", this);
39581             },
39582             scope: this
39583         });
39584
39585         var view = this.grid.view;
39586         view.on("refresh", this.onRefresh, this);
39587         view.on("rowupdated", this.onRowUpdated, this);
39588         view.on("rowremoved", this.onRemove, this);
39589     },
39590
39591     // private
39592     onRefresh : function(){
39593         var ds = this.grid.dataSource, i, v = this.grid.view;
39594         var s = this.selections;
39595         s.each(function(r){
39596             if((i = ds.indexOfId(r.id)) != -1){
39597                 v.onRowSelect(i);
39598                 s.add(ds.getAt(i)); // updating the selection relate data
39599             }else{
39600                 s.remove(r);
39601             }
39602         });
39603     },
39604
39605     // private
39606     onRemove : function(v, index, r){
39607         this.selections.remove(r);
39608     },
39609
39610     // private
39611     onRowUpdated : function(v, index, r){
39612         if(this.isSelected(r)){
39613             v.onRowSelect(index);
39614         }
39615     },
39616
39617     /**
39618      * Select records.
39619      * @param {Array} records The records to select
39620      * @param {Boolean} keepExisting (optional) True to keep existing selections
39621      */
39622     selectRecords : function(records, keepExisting){
39623         if(!keepExisting){
39624             this.clearSelections();
39625         }
39626         var ds = this.grid.dataSource;
39627         for(var i = 0, len = records.length; i < len; i++){
39628             this.selectRow(ds.indexOf(records[i]), true);
39629         }
39630     },
39631
39632     /**
39633      * Gets the number of selected rows.
39634      * @return {Number}
39635      */
39636     getCount : function(){
39637         return this.selections.length;
39638     },
39639
39640     /**
39641      * Selects the first row in the grid.
39642      */
39643     selectFirstRow : function(){
39644         this.selectRow(0);
39645     },
39646
39647     /**
39648      * Select the last row.
39649      * @param {Boolean} keepExisting (optional) True to keep existing selections
39650      */
39651     selectLastRow : function(keepExisting){
39652         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39653     },
39654
39655     /**
39656      * Selects the row immediately following the last selected row.
39657      * @param {Boolean} keepExisting (optional) True to keep existing selections
39658      */
39659     selectNext : function(keepExisting){
39660         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39661             this.selectRow(this.last+1, keepExisting);
39662             this.grid.getView().focusRow(this.last);
39663         }
39664     },
39665
39666     /**
39667      * Selects the row that precedes the last selected row.
39668      * @param {Boolean} keepExisting (optional) True to keep existing selections
39669      */
39670     selectPrevious : function(keepExisting){
39671         if(this.last){
39672             this.selectRow(this.last-1, keepExisting);
39673             this.grid.getView().focusRow(this.last);
39674         }
39675     },
39676
39677     /**
39678      * Returns the selected records
39679      * @return {Array} Array of selected records
39680      */
39681     getSelections : function(){
39682         return [].concat(this.selections.items);
39683     },
39684
39685     /**
39686      * Returns the first selected record.
39687      * @return {Record}
39688      */
39689     getSelected : function(){
39690         return this.selections.itemAt(0);
39691     },
39692
39693
39694     /**
39695      * Clears all selections.
39696      */
39697     clearSelections : function(fast){
39698         if(this.locked) {
39699             return;
39700         }
39701         if(fast !== true){
39702             var ds = this.grid.dataSource;
39703             var s = this.selections;
39704             s.each(function(r){
39705                 this.deselectRow(ds.indexOfId(r.id));
39706             }, this);
39707             s.clear();
39708         }else{
39709             this.selections.clear();
39710         }
39711         this.last = false;
39712     },
39713
39714
39715     /**
39716      * Selects all rows.
39717      */
39718     selectAll : function(){
39719         if(this.locked) {
39720             return;
39721         }
39722         this.selections.clear();
39723         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39724             this.selectRow(i, true);
39725         }
39726     },
39727
39728     /**
39729      * Returns True if there is a selection.
39730      * @return {Boolean}
39731      */
39732     hasSelection : function(){
39733         return this.selections.length > 0;
39734     },
39735
39736     /**
39737      * Returns True if the specified row is selected.
39738      * @param {Number/Record} record The record or index of the record to check
39739      * @return {Boolean}
39740      */
39741     isSelected : function(index){
39742         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39743         return (r && this.selections.key(r.id) ? true : false);
39744     },
39745
39746     /**
39747      * Returns True if the specified record id is selected.
39748      * @param {String} id The id of record to check
39749      * @return {Boolean}
39750      */
39751     isIdSelected : function(id){
39752         return (this.selections.key(id) ? true : false);
39753     },
39754
39755     // private
39756     handleMouseDown : function(e, t){
39757         var view = this.grid.getView(), rowIndex;
39758         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39759             return;
39760         };
39761         if(e.shiftKey && this.last !== false){
39762             var last = this.last;
39763             this.selectRange(last, rowIndex, e.ctrlKey);
39764             this.last = last; // reset the last
39765             view.focusRow(rowIndex);
39766         }else{
39767             var isSelected = this.isSelected(rowIndex);
39768             if(e.button !== 0 && isSelected){
39769                 view.focusRow(rowIndex);
39770             }else if(e.ctrlKey && isSelected){
39771                 this.deselectRow(rowIndex);
39772             }else if(!isSelected){
39773                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39774                 view.focusRow(rowIndex);
39775             }
39776         }
39777         this.fireEvent("afterselectionchange", this);
39778     },
39779     // private
39780     handleDragableRowClick :  function(grid, rowIndex, e) 
39781     {
39782         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39783             this.selectRow(rowIndex, false);
39784             grid.view.focusRow(rowIndex);
39785              this.fireEvent("afterselectionchange", this);
39786         }
39787     },
39788     
39789     /**
39790      * Selects multiple rows.
39791      * @param {Array} rows Array of the indexes of the row to select
39792      * @param {Boolean} keepExisting (optional) True to keep existing selections
39793      */
39794     selectRows : function(rows, keepExisting){
39795         if(!keepExisting){
39796             this.clearSelections();
39797         }
39798         for(var i = 0, len = rows.length; i < len; i++){
39799             this.selectRow(rows[i], true);
39800         }
39801     },
39802
39803     /**
39804      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39805      * @param {Number} startRow The index of the first row in the range
39806      * @param {Number} endRow The index of the last row in the range
39807      * @param {Boolean} keepExisting (optional) True to retain existing selections
39808      */
39809     selectRange : function(startRow, endRow, keepExisting){
39810         if(this.locked) {
39811             return;
39812         }
39813         if(!keepExisting){
39814             this.clearSelections();
39815         }
39816         if(startRow <= endRow){
39817             for(var i = startRow; i <= endRow; i++){
39818                 this.selectRow(i, true);
39819             }
39820         }else{
39821             for(var i = startRow; i >= endRow; i--){
39822                 this.selectRow(i, true);
39823             }
39824         }
39825     },
39826
39827     /**
39828      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39829      * @param {Number} startRow The index of the first row in the range
39830      * @param {Number} endRow The index of the last row in the range
39831      */
39832     deselectRange : function(startRow, endRow, preventViewNotify){
39833         if(this.locked) {
39834             return;
39835         }
39836         for(var i = startRow; i <= endRow; i++){
39837             this.deselectRow(i, preventViewNotify);
39838         }
39839     },
39840
39841     /**
39842      * Selects a row.
39843      * @param {Number} row The index of the row to select
39844      * @param {Boolean} keepExisting (optional) True to keep existing selections
39845      */
39846     selectRow : function(index, keepExisting, preventViewNotify){
39847         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
39848             return;
39849         }
39850         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39851             if(!keepExisting || this.singleSelect){
39852                 this.clearSelections();
39853             }
39854             var r = this.grid.dataSource.getAt(index);
39855             this.selections.add(r);
39856             this.last = this.lastActive = index;
39857             if(!preventViewNotify){
39858                 this.grid.getView().onRowSelect(index);
39859             }
39860             this.fireEvent("rowselect", this, index, r);
39861             this.fireEvent("selectionchange", this);
39862         }
39863     },
39864
39865     /**
39866      * Deselects a row.
39867      * @param {Number} row The index of the row to deselect
39868      */
39869     deselectRow : function(index, preventViewNotify){
39870         if(this.locked) {
39871             return;
39872         }
39873         if(this.last == index){
39874             this.last = false;
39875         }
39876         if(this.lastActive == index){
39877             this.lastActive = false;
39878         }
39879         var r = this.grid.dataSource.getAt(index);
39880         this.selections.remove(r);
39881         if(!preventViewNotify){
39882             this.grid.getView().onRowDeselect(index);
39883         }
39884         this.fireEvent("rowdeselect", this, index);
39885         this.fireEvent("selectionchange", this);
39886     },
39887
39888     // private
39889     restoreLast : function(){
39890         if(this._last){
39891             this.last = this._last;
39892         }
39893     },
39894
39895     // private
39896     acceptsNav : function(row, col, cm){
39897         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39898     },
39899
39900     // private
39901     onEditorKey : function(field, e){
39902         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39903         if(k == e.TAB){
39904             e.stopEvent();
39905             ed.completeEdit();
39906             if(e.shiftKey){
39907                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39908             }else{
39909                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39910             }
39911         }else if(k == e.ENTER && !e.ctrlKey){
39912             e.stopEvent();
39913             ed.completeEdit();
39914             if(e.shiftKey){
39915                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39916             }else{
39917                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39918             }
39919         }else if(k == e.ESC){
39920             ed.cancelEdit();
39921         }
39922         if(newCell){
39923             g.startEditing(newCell[0], newCell[1]);
39924         }
39925     }
39926 });/*
39927  * Based on:
39928  * Ext JS Library 1.1.1
39929  * Copyright(c) 2006-2007, Ext JS, LLC.
39930  *
39931  * Originally Released Under LGPL - original licence link has changed is not relivant.
39932  *
39933  * Fork - LGPL
39934  * <script type="text/javascript">
39935  */
39936 /**
39937  * @class Roo.grid.CellSelectionModel
39938  * @extends Roo.grid.AbstractSelectionModel
39939  * This class provides the basic implementation for cell selection in a grid.
39940  * @constructor
39941  * @param {Object} config The object containing the configuration of this model.
39942  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39943  */
39944 Roo.grid.CellSelectionModel = function(config){
39945     Roo.apply(this, config);
39946
39947     this.selection = null;
39948
39949     this.addEvents({
39950         /**
39951              * @event beforerowselect
39952              * Fires before a cell is selected.
39953              * @param {SelectionModel} this
39954              * @param {Number} rowIndex The selected row index
39955              * @param {Number} colIndex The selected cell index
39956              */
39957             "beforecellselect" : true,
39958         /**
39959              * @event cellselect
39960              * Fires when a cell is selected.
39961              * @param {SelectionModel} this
39962              * @param {Number} rowIndex The selected row index
39963              * @param {Number} colIndex The selected cell index
39964              */
39965             "cellselect" : true,
39966         /**
39967              * @event selectionchange
39968              * Fires when the active selection changes.
39969              * @param {SelectionModel} this
39970              * @param {Object} selection null for no selection or an object (o) with two properties
39971                 <ul>
39972                 <li>o.record: the record object for the row the selection is in</li>
39973                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39974                 </ul>
39975              */
39976             "selectionchange" : true,
39977         /**
39978              * @event tabend
39979              * Fires when the tab (or enter) was pressed on the last editable cell
39980              * You can use this to trigger add new row.
39981              * @param {SelectionModel} this
39982              */
39983             "tabend" : true,
39984          /**
39985              * @event beforeeditnext
39986              * Fires before the next editable sell is made active
39987              * You can use this to skip to another cell or fire the tabend
39988              *    if you set cell to false
39989              * @param {Object} eventdata object : { cell : [ row, col ] } 
39990              */
39991             "beforeeditnext" : true
39992     });
39993     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39994 };
39995
39996 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39997     
39998     enter_is_tab: false,
39999
40000     /** @ignore */
40001     initEvents : function(){
40002         this.grid.on("mousedown", this.handleMouseDown, this);
40003         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40004         var view = this.grid.view;
40005         view.on("refresh", this.onViewChange, this);
40006         view.on("rowupdated", this.onRowUpdated, this);
40007         view.on("beforerowremoved", this.clearSelections, this);
40008         view.on("beforerowsinserted", this.clearSelections, this);
40009         if(this.grid.isEditor){
40010             this.grid.on("beforeedit", this.beforeEdit,  this);
40011         }
40012     },
40013
40014         //private
40015     beforeEdit : function(e){
40016         this.select(e.row, e.column, false, true, e.record);
40017     },
40018
40019         //private
40020     onRowUpdated : function(v, index, r){
40021         if(this.selection && this.selection.record == r){
40022             v.onCellSelect(index, this.selection.cell[1]);
40023         }
40024     },
40025
40026         //private
40027     onViewChange : function(){
40028         this.clearSelections(true);
40029     },
40030
40031         /**
40032          * Returns the currently selected cell,.
40033          * @return {Array} The selected cell (row, column) or null if none selected.
40034          */
40035     getSelectedCell : function(){
40036         return this.selection ? this.selection.cell : null;
40037     },
40038
40039     /**
40040      * Clears all selections.
40041      * @param {Boolean} true to prevent the gridview from being notified about the change.
40042      */
40043     clearSelections : function(preventNotify){
40044         var s = this.selection;
40045         if(s){
40046             if(preventNotify !== true){
40047                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40048             }
40049             this.selection = null;
40050             this.fireEvent("selectionchange", this, null);
40051         }
40052     },
40053
40054     /**
40055      * Returns true if there is a selection.
40056      * @return {Boolean}
40057      */
40058     hasSelection : function(){
40059         return this.selection ? true : false;
40060     },
40061
40062     /** @ignore */
40063     handleMouseDown : function(e, t){
40064         var v = this.grid.getView();
40065         if(this.isLocked()){
40066             return;
40067         };
40068         var row = v.findRowIndex(t);
40069         var cell = v.findCellIndex(t);
40070         if(row !== false && cell !== false){
40071             this.select(row, cell);
40072         }
40073     },
40074
40075     /**
40076      * Selects a cell.
40077      * @param {Number} rowIndex
40078      * @param {Number} collIndex
40079      */
40080     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40081         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40082             this.clearSelections();
40083             r = r || this.grid.dataSource.getAt(rowIndex);
40084             this.selection = {
40085                 record : r,
40086                 cell : [rowIndex, colIndex]
40087             };
40088             if(!preventViewNotify){
40089                 var v = this.grid.getView();
40090                 v.onCellSelect(rowIndex, colIndex);
40091                 if(preventFocus !== true){
40092                     v.focusCell(rowIndex, colIndex);
40093                 }
40094             }
40095             this.fireEvent("cellselect", this, rowIndex, colIndex);
40096             this.fireEvent("selectionchange", this, this.selection);
40097         }
40098     },
40099
40100         //private
40101     isSelectable : function(rowIndex, colIndex, cm){
40102         return !cm.isHidden(colIndex);
40103     },
40104
40105     /** @ignore */
40106     handleKeyDown : function(e){
40107         //Roo.log('Cell Sel Model handleKeyDown');
40108         if(!e.isNavKeyPress()){
40109             return;
40110         }
40111         var g = this.grid, s = this.selection;
40112         if(!s){
40113             e.stopEvent();
40114             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40115             if(cell){
40116                 this.select(cell[0], cell[1]);
40117             }
40118             return;
40119         }
40120         var sm = this;
40121         var walk = function(row, col, step){
40122             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40123         };
40124         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40125         var newCell;
40126
40127       
40128
40129         switch(k){
40130             case e.TAB:
40131                 // handled by onEditorKey
40132                 if (g.isEditor && g.editing) {
40133                     return;
40134                 }
40135                 if(e.shiftKey) {
40136                     newCell = walk(r, c-1, -1);
40137                 } else {
40138                     newCell = walk(r, c+1, 1);
40139                 }
40140                 break;
40141             
40142             case e.DOWN:
40143                newCell = walk(r+1, c, 1);
40144                 break;
40145             
40146             case e.UP:
40147                 newCell = walk(r-1, c, -1);
40148                 break;
40149             
40150             case e.RIGHT:
40151                 newCell = walk(r, c+1, 1);
40152                 break;
40153             
40154             case e.LEFT:
40155                 newCell = walk(r, c-1, -1);
40156                 break;
40157             
40158             case e.ENTER:
40159                 
40160                 if(g.isEditor && !g.editing){
40161                    g.startEditing(r, c);
40162                    e.stopEvent();
40163                    return;
40164                 }
40165                 
40166                 
40167              break;
40168         };
40169         if(newCell){
40170             this.select(newCell[0], newCell[1]);
40171             e.stopEvent();
40172             
40173         }
40174     },
40175
40176     acceptsNav : function(row, col, cm){
40177         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40178     },
40179     /**
40180      * Selects a cell.
40181      * @param {Number} field (not used) - as it's normally used as a listener
40182      * @param {Number} e - event - fake it by using
40183      *
40184      * var e = Roo.EventObjectImpl.prototype;
40185      * e.keyCode = e.TAB
40186      *
40187      * 
40188      */
40189     onEditorKey : function(field, e){
40190         
40191         var k = e.getKey(),
40192             newCell,
40193             g = this.grid,
40194             ed = g.activeEditor,
40195             forward = false;
40196         ///Roo.log('onEditorKey' + k);
40197         
40198         
40199         if (this.enter_is_tab && k == e.ENTER) {
40200             k = e.TAB;
40201         }
40202         
40203         if(k == e.TAB){
40204             if(e.shiftKey){
40205                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40206             }else{
40207                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40208                 forward = true;
40209             }
40210             
40211             e.stopEvent();
40212             
40213         } else if(k == e.ENTER &&  !e.ctrlKey){
40214             ed.completeEdit();
40215             e.stopEvent();
40216             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40217         
40218                 } else if(k == e.ESC){
40219             ed.cancelEdit();
40220         }
40221                 
40222         if (newCell) {
40223             var ecall = { cell : newCell, forward : forward };
40224             this.fireEvent('beforeeditnext', ecall );
40225             newCell = ecall.cell;
40226                         forward = ecall.forward;
40227         }
40228                 
40229         if(newCell){
40230             //Roo.log('next cell after edit');
40231             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40232         } else if (forward) {
40233             // tabbed past last
40234             this.fireEvent.defer(100, this, ['tabend',this]);
40235         }
40236     }
40237 });/*
40238  * Based on:
40239  * Ext JS Library 1.1.1
40240  * Copyright(c) 2006-2007, Ext JS, LLC.
40241  *
40242  * Originally Released Under LGPL - original licence link has changed is not relivant.
40243  *
40244  * Fork - LGPL
40245  * <script type="text/javascript">
40246  */
40247  
40248 /**
40249  * @class Roo.grid.EditorGrid
40250  * @extends Roo.grid.Grid
40251  * Class for creating and editable grid.
40252  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40253  * The container MUST have some type of size defined for the grid to fill. The container will be 
40254  * automatically set to position relative if it isn't already.
40255  * @param {Object} dataSource The data model to bind to
40256  * @param {Object} colModel The column model with info about this grid's columns
40257  */
40258 Roo.grid.EditorGrid = function(container, config){
40259     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40260     this.getGridEl().addClass("xedit-grid");
40261
40262     if(!this.selModel){
40263         this.selModel = new Roo.grid.CellSelectionModel();
40264     }
40265
40266     this.activeEditor = null;
40267
40268         this.addEvents({
40269             /**
40270              * @event beforeedit
40271              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40272              * <ul style="padding:5px;padding-left:16px;">
40273              * <li>grid - This grid</li>
40274              * <li>record - The record being edited</li>
40275              * <li>field - The field name being edited</li>
40276              * <li>value - The value for the field being edited.</li>
40277              * <li>row - The grid row index</li>
40278              * <li>column - The grid column index</li>
40279              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40280              * </ul>
40281              * @param {Object} e An edit event (see above for description)
40282              */
40283             "beforeedit" : true,
40284             /**
40285              * @event afteredit
40286              * Fires after a cell is edited. <br />
40287              * <ul style="padding:5px;padding-left:16px;">
40288              * <li>grid - This grid</li>
40289              * <li>record - The record being edited</li>
40290              * <li>field - The field name being edited</li>
40291              * <li>value - The value being set</li>
40292              * <li>originalValue - The original value for the field, before the edit.</li>
40293              * <li>row - The grid row index</li>
40294              * <li>column - The grid column index</li>
40295              * </ul>
40296              * @param {Object} e An edit event (see above for description)
40297              */
40298             "afteredit" : true,
40299             /**
40300              * @event validateedit
40301              * Fires after a cell is edited, but before the value is set in the record. 
40302          * You can use this to modify the value being set in the field, Return false
40303              * to cancel the change. The edit event object has the following properties <br />
40304              * <ul style="padding:5px;padding-left:16px;">
40305          * <li>editor - This editor</li>
40306              * <li>grid - This grid</li>
40307              * <li>record - The record being edited</li>
40308              * <li>field - The field name being edited</li>
40309              * <li>value - The value being set</li>
40310              * <li>originalValue - The original value for the field, before the edit.</li>
40311              * <li>row - The grid row index</li>
40312              * <li>column - The grid column index</li>
40313              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40314              * </ul>
40315              * @param {Object} e An edit event (see above for description)
40316              */
40317             "validateedit" : true
40318         });
40319     this.on("bodyscroll", this.stopEditing,  this);
40320     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40321 };
40322
40323 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40324     /**
40325      * @cfg {Number} clicksToEdit
40326      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40327      */
40328     clicksToEdit: 2,
40329
40330     // private
40331     isEditor : true,
40332     // private
40333     trackMouseOver: false, // causes very odd FF errors
40334
40335     onCellDblClick : function(g, row, col){
40336         this.startEditing(row, col);
40337     },
40338
40339     onEditComplete : function(ed, value, startValue){
40340         this.editing = false;
40341         this.activeEditor = null;
40342         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40343         var r = ed.record;
40344         var field = this.colModel.getDataIndex(ed.col);
40345         var e = {
40346             grid: this,
40347             record: r,
40348             field: field,
40349             originalValue: startValue,
40350             value: value,
40351             row: ed.row,
40352             column: ed.col,
40353             cancel:false,
40354             editor: ed
40355         };
40356         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
40357         cell.show();
40358           
40359         if(String(value) !== String(startValue)){
40360             
40361             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40362                 r.set(field, e.value);
40363                 // if we are dealing with a combo box..
40364                 // then we also set the 'name' colum to be the displayField
40365                 if (ed.field.displayField && ed.field.name) {
40366                     r.set(ed.field.name, ed.field.el.dom.value);
40367                 }
40368                 
40369                 delete e.cancel; //?? why!!!
40370                 this.fireEvent("afteredit", e);
40371             }
40372         } else {
40373             this.fireEvent("afteredit", e); // always fire it!
40374         }
40375         this.view.focusCell(ed.row, ed.col);
40376     },
40377
40378     /**
40379      * Starts editing the specified for the specified row/column
40380      * @param {Number} rowIndex
40381      * @param {Number} colIndex
40382      */
40383     startEditing : function(row, col){
40384         this.stopEditing();
40385         if(this.colModel.isCellEditable(col, row)){
40386             this.view.ensureVisible(row, col, true);
40387           
40388             var r = this.dataSource.getAt(row);
40389             var field = this.colModel.getDataIndex(col);
40390             var cell = Roo.get(this.view.getCell(row,col));
40391             var e = {
40392                 grid: this,
40393                 record: r,
40394                 field: field,
40395                 value: r.data[field],
40396                 row: row,
40397                 column: col,
40398                 cancel:false 
40399             };
40400             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40401                 this.editing = true;
40402                 var ed = this.colModel.getCellEditor(col, row);
40403                 
40404                 if (!ed) {
40405                     return;
40406                 }
40407                 if(!ed.rendered){
40408                     ed.render(ed.parentEl || document.body);
40409                 }
40410                 ed.field.reset();
40411                
40412                 cell.hide();
40413                 
40414                 (function(){ // complex but required for focus issues in safari, ie and opera
40415                     ed.row = row;
40416                     ed.col = col;
40417                     ed.record = r;
40418                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40419                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40420                     this.activeEditor = ed;
40421                     var v = r.data[field];
40422                     ed.startEdit(this.view.getCell(row, col), v);
40423                     // combo's with 'displayField and name set
40424                     if (ed.field.displayField && ed.field.name) {
40425                         ed.field.el.dom.value = r.data[ed.field.name];
40426                     }
40427                     
40428                     
40429                 }).defer(50, this);
40430             }
40431         }
40432     },
40433         
40434     /**
40435      * Stops any active editing
40436      */
40437     stopEditing : function(){
40438         if(this.activeEditor){
40439             this.activeEditor.completeEdit();
40440         }
40441         this.activeEditor = null;
40442     },
40443         
40444          /**
40445      * Called to get grid's drag proxy text, by default returns this.ddText.
40446      * @return {String}
40447      */
40448     getDragDropText : function(){
40449         var count = this.selModel.getSelectedCell() ? 1 : 0;
40450         return String.format(this.ddText, count, count == 1 ? '' : 's');
40451     }
40452         
40453 });/*
40454  * Based on:
40455  * Ext JS Library 1.1.1
40456  * Copyright(c) 2006-2007, Ext JS, LLC.
40457  *
40458  * Originally Released Under LGPL - original licence link has changed is not relivant.
40459  *
40460  * Fork - LGPL
40461  * <script type="text/javascript">
40462  */
40463
40464 // private - not really -- you end up using it !
40465 // This is a support class used internally by the Grid components
40466
40467 /**
40468  * @class Roo.grid.GridEditor
40469  * @extends Roo.Editor
40470  * Class for creating and editable grid elements.
40471  * @param {Object} config any settings (must include field)
40472  */
40473 Roo.grid.GridEditor = function(field, config){
40474     if (!config && field.field) {
40475         config = field;
40476         field = Roo.factory(config.field, Roo.form);
40477     }
40478     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40479     field.monitorTab = false;
40480 };
40481
40482 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40483     
40484     /**
40485      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40486      */
40487     
40488     alignment: "tl-tl",
40489     autoSize: "width",
40490     hideEl : false,
40491     cls: "x-small-editor x-grid-editor",
40492     shim:false,
40493     shadow:"frame"
40494 });/*
40495  * Based on:
40496  * Ext JS Library 1.1.1
40497  * Copyright(c) 2006-2007, Ext JS, LLC.
40498  *
40499  * Originally Released Under LGPL - original licence link has changed is not relivant.
40500  *
40501  * Fork - LGPL
40502  * <script type="text/javascript">
40503  */
40504   
40505
40506   
40507 Roo.grid.PropertyRecord = Roo.data.Record.create([
40508     {name:'name',type:'string'},  'value'
40509 ]);
40510
40511
40512 Roo.grid.PropertyStore = function(grid, source){
40513     this.grid = grid;
40514     this.store = new Roo.data.Store({
40515         recordType : Roo.grid.PropertyRecord
40516     });
40517     this.store.on('update', this.onUpdate,  this);
40518     if(source){
40519         this.setSource(source);
40520     }
40521     Roo.grid.PropertyStore.superclass.constructor.call(this);
40522 };
40523
40524
40525
40526 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40527     setSource : function(o){
40528         this.source = o;
40529         this.store.removeAll();
40530         var data = [];
40531         for(var k in o){
40532             if(this.isEditableValue(o[k])){
40533                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40534             }
40535         }
40536         this.store.loadRecords({records: data}, {}, true);
40537     },
40538
40539     onUpdate : function(ds, record, type){
40540         if(type == Roo.data.Record.EDIT){
40541             var v = record.data['value'];
40542             var oldValue = record.modified['value'];
40543             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40544                 this.source[record.id] = v;
40545                 record.commit();
40546                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40547             }else{
40548                 record.reject();
40549             }
40550         }
40551     },
40552
40553     getProperty : function(row){
40554        return this.store.getAt(row);
40555     },
40556
40557     isEditableValue: function(val){
40558         if(val && val instanceof Date){
40559             return true;
40560         }else if(typeof val == 'object' || typeof val == 'function'){
40561             return false;
40562         }
40563         return true;
40564     },
40565
40566     setValue : function(prop, value){
40567         this.source[prop] = value;
40568         this.store.getById(prop).set('value', value);
40569     },
40570
40571     getSource : function(){
40572         return this.source;
40573     }
40574 });
40575
40576 Roo.grid.PropertyColumnModel = function(grid, store){
40577     this.grid = grid;
40578     var g = Roo.grid;
40579     g.PropertyColumnModel.superclass.constructor.call(this, [
40580         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40581         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40582     ]);
40583     this.store = store;
40584     this.bselect = Roo.DomHelper.append(document.body, {
40585         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40586             {tag: 'option', value: 'true', html: 'true'},
40587             {tag: 'option', value: 'false', html: 'false'}
40588         ]
40589     });
40590     Roo.id(this.bselect);
40591     var f = Roo.form;
40592     this.editors = {
40593         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40594         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40595         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40596         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40597         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40598     };
40599     this.renderCellDelegate = this.renderCell.createDelegate(this);
40600     this.renderPropDelegate = this.renderProp.createDelegate(this);
40601 };
40602
40603 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40604     
40605     
40606     nameText : 'Name',
40607     valueText : 'Value',
40608     
40609     dateFormat : 'm/j/Y',
40610     
40611     
40612     renderDate : function(dateVal){
40613         return dateVal.dateFormat(this.dateFormat);
40614     },
40615
40616     renderBool : function(bVal){
40617         return bVal ? 'true' : 'false';
40618     },
40619
40620     isCellEditable : function(colIndex, rowIndex){
40621         return colIndex == 1;
40622     },
40623
40624     getRenderer : function(col){
40625         return col == 1 ?
40626             this.renderCellDelegate : this.renderPropDelegate;
40627     },
40628
40629     renderProp : function(v){
40630         return this.getPropertyName(v);
40631     },
40632
40633     renderCell : function(val){
40634         var rv = val;
40635         if(val instanceof Date){
40636             rv = this.renderDate(val);
40637         }else if(typeof val == 'boolean'){
40638             rv = this.renderBool(val);
40639         }
40640         return Roo.util.Format.htmlEncode(rv);
40641     },
40642
40643     getPropertyName : function(name){
40644         var pn = this.grid.propertyNames;
40645         return pn && pn[name] ? pn[name] : name;
40646     },
40647
40648     getCellEditor : function(colIndex, rowIndex){
40649         var p = this.store.getProperty(rowIndex);
40650         var n = p.data['name'], val = p.data['value'];
40651         
40652         if(typeof(this.grid.customEditors[n]) == 'string'){
40653             return this.editors[this.grid.customEditors[n]];
40654         }
40655         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40656             return this.grid.customEditors[n];
40657         }
40658         if(val instanceof Date){
40659             return this.editors['date'];
40660         }else if(typeof val == 'number'){
40661             return this.editors['number'];
40662         }else if(typeof val == 'boolean'){
40663             return this.editors['boolean'];
40664         }else{
40665             return this.editors['string'];
40666         }
40667     }
40668 });
40669
40670 /**
40671  * @class Roo.grid.PropertyGrid
40672  * @extends Roo.grid.EditorGrid
40673  * This class represents the  interface of a component based property grid control.
40674  * <br><br>Usage:<pre><code>
40675  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40676       
40677  });
40678  // set any options
40679  grid.render();
40680  * </code></pre>
40681   
40682  * @constructor
40683  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40684  * The container MUST have some type of size defined for the grid to fill. The container will be
40685  * automatically set to position relative if it isn't already.
40686  * @param {Object} config A config object that sets properties on this grid.
40687  */
40688 Roo.grid.PropertyGrid = function(container, config){
40689     config = config || {};
40690     var store = new Roo.grid.PropertyStore(this);
40691     this.store = store;
40692     var cm = new Roo.grid.PropertyColumnModel(this, store);
40693     store.store.sort('name', 'ASC');
40694     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40695         ds: store.store,
40696         cm: cm,
40697         enableColLock:false,
40698         enableColumnMove:false,
40699         stripeRows:false,
40700         trackMouseOver: false,
40701         clicksToEdit:1
40702     }, config));
40703     this.getGridEl().addClass('x-props-grid');
40704     this.lastEditRow = null;
40705     this.on('columnresize', this.onColumnResize, this);
40706     this.addEvents({
40707          /**
40708              * @event beforepropertychange
40709              * Fires before a property changes (return false to stop?)
40710              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40711              * @param {String} id Record Id
40712              * @param {String} newval New Value
40713          * @param {String} oldval Old Value
40714              */
40715         "beforepropertychange": true,
40716         /**
40717              * @event propertychange
40718              * Fires after a property changes
40719              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40720              * @param {String} id Record Id
40721              * @param {String} newval New Value
40722          * @param {String} oldval Old Value
40723              */
40724         "propertychange": true
40725     });
40726     this.customEditors = this.customEditors || {};
40727 };
40728 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40729     
40730      /**
40731      * @cfg {Object} customEditors map of colnames=> custom editors.
40732      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40733      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40734      * false disables editing of the field.
40735          */
40736     
40737       /**
40738      * @cfg {Object} propertyNames map of property Names to their displayed value
40739          */
40740     
40741     render : function(){
40742         Roo.grid.PropertyGrid.superclass.render.call(this);
40743         this.autoSize.defer(100, this);
40744     },
40745
40746     autoSize : function(){
40747         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40748         if(this.view){
40749             this.view.fitColumns();
40750         }
40751     },
40752
40753     onColumnResize : function(){
40754         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40755         this.autoSize();
40756     },
40757     /**
40758      * Sets the data for the Grid
40759      * accepts a Key => Value object of all the elements avaiable.
40760      * @param {Object} data  to appear in grid.
40761      */
40762     setSource : function(source){
40763         this.store.setSource(source);
40764         //this.autoSize();
40765     },
40766     /**
40767      * Gets all the data from the grid.
40768      * @return {Object} data  data stored in grid
40769      */
40770     getSource : function(){
40771         return this.store.getSource();
40772     }
40773 });/*
40774   
40775  * Licence LGPL
40776  
40777  */
40778  
40779 /**
40780  * @class Roo.grid.Calendar
40781  * @extends Roo.util.Grid
40782  * This class extends the Grid to provide a calendar widget
40783  * <br><br>Usage:<pre><code>
40784  var grid = new Roo.grid.Calendar("my-container-id", {
40785      ds: myDataStore,
40786      cm: myColModel,
40787      selModel: mySelectionModel,
40788      autoSizeColumns: true,
40789      monitorWindowResize: false,
40790      trackMouseOver: true
40791      eventstore : real data store..
40792  });
40793  // set any options
40794  grid.render();
40795   
40796   * @constructor
40797  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40798  * The container MUST have some type of size defined for the grid to fill. The container will be
40799  * automatically set to position relative if it isn't already.
40800  * @param {Object} config A config object that sets properties on this grid.
40801  */
40802 Roo.grid.Calendar = function(container, config){
40803         // initialize the container
40804         this.container = Roo.get(container);
40805         this.container.update("");
40806         this.container.setStyle("overflow", "hidden");
40807     this.container.addClass('x-grid-container');
40808
40809     this.id = this.container.id;
40810
40811     Roo.apply(this, config);
40812     // check and correct shorthanded configs
40813     
40814     var rows = [];
40815     var d =1;
40816     for (var r = 0;r < 6;r++) {
40817         
40818         rows[r]=[];
40819         for (var c =0;c < 7;c++) {
40820             rows[r][c]= '';
40821         }
40822     }
40823     if (this.eventStore) {
40824         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40825         this.eventStore.on('load',this.onLoad, this);
40826         this.eventStore.on('beforeload',this.clearEvents, this);
40827          
40828     }
40829     
40830     this.dataSource = new Roo.data.Store({
40831             proxy: new Roo.data.MemoryProxy(rows),
40832             reader: new Roo.data.ArrayReader({}, [
40833                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40834     });
40835
40836     this.dataSource.load();
40837     this.ds = this.dataSource;
40838     this.ds.xmodule = this.xmodule || false;
40839     
40840     
40841     var cellRender = function(v,x,r)
40842     {
40843         return String.format(
40844             '<div class="fc-day  fc-widget-content"><div>' +
40845                 '<div class="fc-event-container"></div>' +
40846                 '<div class="fc-day-number">{0}</div>'+
40847                 
40848                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40849             '</div></div>', v);
40850     
40851     }
40852     
40853     
40854     this.colModel = new Roo.grid.ColumnModel( [
40855         {
40856             xtype: 'ColumnModel',
40857             xns: Roo.grid,
40858             dataIndex : 'weekday0',
40859             header : 'Sunday',
40860             renderer : cellRender
40861         },
40862         {
40863             xtype: 'ColumnModel',
40864             xns: Roo.grid,
40865             dataIndex : 'weekday1',
40866             header : 'Monday',
40867             renderer : cellRender
40868         },
40869         {
40870             xtype: 'ColumnModel',
40871             xns: Roo.grid,
40872             dataIndex : 'weekday2',
40873             header : 'Tuesday',
40874             renderer : cellRender
40875         },
40876         {
40877             xtype: 'ColumnModel',
40878             xns: Roo.grid,
40879             dataIndex : 'weekday3',
40880             header : 'Wednesday',
40881             renderer : cellRender
40882         },
40883         {
40884             xtype: 'ColumnModel',
40885             xns: Roo.grid,
40886             dataIndex : 'weekday4',
40887             header : 'Thursday',
40888             renderer : cellRender
40889         },
40890         {
40891             xtype: 'ColumnModel',
40892             xns: Roo.grid,
40893             dataIndex : 'weekday5',
40894             header : 'Friday',
40895             renderer : cellRender
40896         },
40897         {
40898             xtype: 'ColumnModel',
40899             xns: Roo.grid,
40900             dataIndex : 'weekday6',
40901             header : 'Saturday',
40902             renderer : cellRender
40903         }
40904     ]);
40905     this.cm = this.colModel;
40906     this.cm.xmodule = this.xmodule || false;
40907  
40908         
40909           
40910     //this.selModel = new Roo.grid.CellSelectionModel();
40911     //this.sm = this.selModel;
40912     //this.selModel.init(this);
40913     
40914     
40915     if(this.width){
40916         this.container.setWidth(this.width);
40917     }
40918
40919     if(this.height){
40920         this.container.setHeight(this.height);
40921     }
40922     /** @private */
40923         this.addEvents({
40924         // raw events
40925         /**
40926          * @event click
40927          * The raw click event for the entire grid.
40928          * @param {Roo.EventObject} e
40929          */
40930         "click" : true,
40931         /**
40932          * @event dblclick
40933          * The raw dblclick event for the entire grid.
40934          * @param {Roo.EventObject} e
40935          */
40936         "dblclick" : true,
40937         /**
40938          * @event contextmenu
40939          * The raw contextmenu event for the entire grid.
40940          * @param {Roo.EventObject} e
40941          */
40942         "contextmenu" : true,
40943         /**
40944          * @event mousedown
40945          * The raw mousedown event for the entire grid.
40946          * @param {Roo.EventObject} e
40947          */
40948         "mousedown" : true,
40949         /**
40950          * @event mouseup
40951          * The raw mouseup event for the entire grid.
40952          * @param {Roo.EventObject} e
40953          */
40954         "mouseup" : true,
40955         /**
40956          * @event mouseover
40957          * The raw mouseover event for the entire grid.
40958          * @param {Roo.EventObject} e
40959          */
40960         "mouseover" : true,
40961         /**
40962          * @event mouseout
40963          * The raw mouseout event for the entire grid.
40964          * @param {Roo.EventObject} e
40965          */
40966         "mouseout" : true,
40967         /**
40968          * @event keypress
40969          * The raw keypress event for the entire grid.
40970          * @param {Roo.EventObject} e
40971          */
40972         "keypress" : true,
40973         /**
40974          * @event keydown
40975          * The raw keydown event for the entire grid.
40976          * @param {Roo.EventObject} e
40977          */
40978         "keydown" : true,
40979
40980         // custom events
40981
40982         /**
40983          * @event cellclick
40984          * Fires when a cell is clicked
40985          * @param {Grid} this
40986          * @param {Number} rowIndex
40987          * @param {Number} columnIndex
40988          * @param {Roo.EventObject} e
40989          */
40990         "cellclick" : true,
40991         /**
40992          * @event celldblclick
40993          * Fires when a cell is double clicked
40994          * @param {Grid} this
40995          * @param {Number} rowIndex
40996          * @param {Number} columnIndex
40997          * @param {Roo.EventObject} e
40998          */
40999         "celldblclick" : true,
41000         /**
41001          * @event rowclick
41002          * Fires when a row is clicked
41003          * @param {Grid} this
41004          * @param {Number} rowIndex
41005          * @param {Roo.EventObject} e
41006          */
41007         "rowclick" : true,
41008         /**
41009          * @event rowdblclick
41010          * Fires when a row is double clicked
41011          * @param {Grid} this
41012          * @param {Number} rowIndex
41013          * @param {Roo.EventObject} e
41014          */
41015         "rowdblclick" : true,
41016         /**
41017          * @event headerclick
41018          * Fires when a header is clicked
41019          * @param {Grid} this
41020          * @param {Number} columnIndex
41021          * @param {Roo.EventObject} e
41022          */
41023         "headerclick" : true,
41024         /**
41025          * @event headerdblclick
41026          * Fires when a header cell is double clicked
41027          * @param {Grid} this
41028          * @param {Number} columnIndex
41029          * @param {Roo.EventObject} e
41030          */
41031         "headerdblclick" : true,
41032         /**
41033          * @event rowcontextmenu
41034          * Fires when a row is right clicked
41035          * @param {Grid} this
41036          * @param {Number} rowIndex
41037          * @param {Roo.EventObject} e
41038          */
41039         "rowcontextmenu" : true,
41040         /**
41041          * @event cellcontextmenu
41042          * Fires when a cell is right clicked
41043          * @param {Grid} this
41044          * @param {Number} rowIndex
41045          * @param {Number} cellIndex
41046          * @param {Roo.EventObject} e
41047          */
41048          "cellcontextmenu" : true,
41049         /**
41050          * @event headercontextmenu
41051          * Fires when a header is right clicked
41052          * @param {Grid} this
41053          * @param {Number} columnIndex
41054          * @param {Roo.EventObject} e
41055          */
41056         "headercontextmenu" : true,
41057         /**
41058          * @event bodyscroll
41059          * Fires when the body element is scrolled
41060          * @param {Number} scrollLeft
41061          * @param {Number} scrollTop
41062          */
41063         "bodyscroll" : true,
41064         /**
41065          * @event columnresize
41066          * Fires when the user resizes a column
41067          * @param {Number} columnIndex
41068          * @param {Number} newSize
41069          */
41070         "columnresize" : true,
41071         /**
41072          * @event columnmove
41073          * Fires when the user moves a column
41074          * @param {Number} oldIndex
41075          * @param {Number} newIndex
41076          */
41077         "columnmove" : true,
41078         /**
41079          * @event startdrag
41080          * Fires when row(s) start being dragged
41081          * @param {Grid} this
41082          * @param {Roo.GridDD} dd The drag drop object
41083          * @param {event} e The raw browser event
41084          */
41085         "startdrag" : true,
41086         /**
41087          * @event enddrag
41088          * Fires when a drag operation is complete
41089          * @param {Grid} this
41090          * @param {Roo.GridDD} dd The drag drop object
41091          * @param {event} e The raw browser event
41092          */
41093         "enddrag" : true,
41094         /**
41095          * @event dragdrop
41096          * Fires when dragged row(s) are dropped on a valid DD target
41097          * @param {Grid} this
41098          * @param {Roo.GridDD} dd The drag drop object
41099          * @param {String} targetId The target drag drop object
41100          * @param {event} e The raw browser event
41101          */
41102         "dragdrop" : true,
41103         /**
41104          * @event dragover
41105          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41106          * @param {Grid} this
41107          * @param {Roo.GridDD} dd The drag drop object
41108          * @param {String} targetId The target drag drop object
41109          * @param {event} e The raw browser event
41110          */
41111         "dragover" : true,
41112         /**
41113          * @event dragenter
41114          *  Fires when the dragged row(s) first cross another DD target while being dragged
41115          * @param {Grid} this
41116          * @param {Roo.GridDD} dd The drag drop object
41117          * @param {String} targetId The target drag drop object
41118          * @param {event} e The raw browser event
41119          */
41120         "dragenter" : true,
41121         /**
41122          * @event dragout
41123          * Fires when the dragged row(s) leave another DD target while being dragged
41124          * @param {Grid} this
41125          * @param {Roo.GridDD} dd The drag drop object
41126          * @param {String} targetId The target drag drop object
41127          * @param {event} e The raw browser event
41128          */
41129         "dragout" : true,
41130         /**
41131          * @event rowclass
41132          * Fires when a row is rendered, so you can change add a style to it.
41133          * @param {GridView} gridview   The grid view
41134          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41135          */
41136         'rowclass' : true,
41137
41138         /**
41139          * @event render
41140          * Fires when the grid is rendered
41141          * @param {Grid} grid
41142          */
41143         'render' : true,
41144             /**
41145              * @event select
41146              * Fires when a date is selected
41147              * @param {DatePicker} this
41148              * @param {Date} date The selected date
41149              */
41150         'select': true,
41151         /**
41152              * @event monthchange
41153              * Fires when the displayed month changes 
41154              * @param {DatePicker} this
41155              * @param {Date} date The selected month
41156              */
41157         'monthchange': true,
41158         /**
41159              * @event evententer
41160              * Fires when mouse over an event
41161              * @param {Calendar} this
41162              * @param {event} Event
41163              */
41164         'evententer': true,
41165         /**
41166              * @event eventleave
41167              * Fires when the mouse leaves an
41168              * @param {Calendar} this
41169              * @param {event}
41170              */
41171         'eventleave': true,
41172         /**
41173              * @event eventclick
41174              * Fires when the mouse click an
41175              * @param {Calendar} this
41176              * @param {event}
41177              */
41178         'eventclick': true,
41179         /**
41180              * @event eventrender
41181              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41182              * @param {Calendar} this
41183              * @param {data} data to be modified
41184              */
41185         'eventrender': true
41186         
41187     });
41188
41189     Roo.grid.Grid.superclass.constructor.call(this);
41190     this.on('render', function() {
41191         this.view.el.addClass('x-grid-cal'); 
41192         
41193         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41194
41195     },this);
41196     
41197     if (!Roo.grid.Calendar.style) {
41198         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41199             
41200             
41201             '.x-grid-cal .x-grid-col' :  {
41202                 height: 'auto !important',
41203                 'vertical-align': 'top'
41204             },
41205             '.x-grid-cal  .fc-event-hori' : {
41206                 height: '14px'
41207             }
41208              
41209             
41210         }, Roo.id());
41211     }
41212
41213     
41214     
41215 };
41216 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41217     /**
41218      * @cfg {Store} eventStore The store that loads events.
41219      */
41220     eventStore : 25,
41221
41222      
41223     activeDate : false,
41224     startDay : 0,
41225     autoWidth : true,
41226     monitorWindowResize : false,
41227
41228     
41229     resizeColumns : function() {
41230         var col = (this.view.el.getWidth() / 7) - 3;
41231         // loop through cols, and setWidth
41232         for(var i =0 ; i < 7 ; i++){
41233             this.cm.setColumnWidth(i, col);
41234         }
41235     },
41236      setDate :function(date) {
41237         
41238         Roo.log('setDate?');
41239         
41240         this.resizeColumns();
41241         var vd = this.activeDate;
41242         this.activeDate = date;
41243 //        if(vd && this.el){
41244 //            var t = date.getTime();
41245 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41246 //                Roo.log('using add remove');
41247 //                
41248 //                this.fireEvent('monthchange', this, date);
41249 //                
41250 //                this.cells.removeClass("fc-state-highlight");
41251 //                this.cells.each(function(c){
41252 //                   if(c.dateValue == t){
41253 //                       c.addClass("fc-state-highlight");
41254 //                       setTimeout(function(){
41255 //                            try{c.dom.firstChild.focus();}catch(e){}
41256 //                       }, 50);
41257 //                       return false;
41258 //                   }
41259 //                   return true;
41260 //                });
41261 //                return;
41262 //            }
41263 //        }
41264         
41265         var days = date.getDaysInMonth();
41266         
41267         var firstOfMonth = date.getFirstDateOfMonth();
41268         var startingPos = firstOfMonth.getDay()-this.startDay;
41269         
41270         if(startingPos < this.startDay){
41271             startingPos += 7;
41272         }
41273         
41274         var pm = date.add(Date.MONTH, -1);
41275         var prevStart = pm.getDaysInMonth()-startingPos;
41276 //        
41277         
41278         
41279         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41280         
41281         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41282         //this.cells.addClassOnOver('fc-state-hover');
41283         
41284         var cells = this.cells.elements;
41285         var textEls = this.textNodes;
41286         
41287         //Roo.each(cells, function(cell){
41288         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41289         //});
41290         
41291         days += startingPos;
41292
41293         // convert everything to numbers so it's fast
41294         var day = 86400000;
41295         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41296         //Roo.log(d);
41297         //Roo.log(pm);
41298         //Roo.log(prevStart);
41299         
41300         var today = new Date().clearTime().getTime();
41301         var sel = date.clearTime().getTime();
41302         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41303         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41304         var ddMatch = this.disabledDatesRE;
41305         var ddText = this.disabledDatesText;
41306         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41307         var ddaysText = this.disabledDaysText;
41308         var format = this.format;
41309         
41310         var setCellClass = function(cal, cell){
41311             
41312             //Roo.log('set Cell Class');
41313             cell.title = "";
41314             var t = d.getTime();
41315             
41316             //Roo.log(d);
41317             
41318             
41319             cell.dateValue = t;
41320             if(t == today){
41321                 cell.className += " fc-today";
41322                 cell.className += " fc-state-highlight";
41323                 cell.title = cal.todayText;
41324             }
41325             if(t == sel){
41326                 // disable highlight in other month..
41327                 cell.className += " fc-state-highlight";
41328                 
41329             }
41330             // disabling
41331             if(t < min) {
41332                 //cell.className = " fc-state-disabled";
41333                 cell.title = cal.minText;
41334                 return;
41335             }
41336             if(t > max) {
41337                 //cell.className = " fc-state-disabled";
41338                 cell.title = cal.maxText;
41339                 return;
41340             }
41341             if(ddays){
41342                 if(ddays.indexOf(d.getDay()) != -1){
41343                     // cell.title = ddaysText;
41344                    // cell.className = " fc-state-disabled";
41345                 }
41346             }
41347             if(ddMatch && format){
41348                 var fvalue = d.dateFormat(format);
41349                 if(ddMatch.test(fvalue)){
41350                     cell.title = ddText.replace("%0", fvalue);
41351                    cell.className = " fc-state-disabled";
41352                 }
41353             }
41354             
41355             if (!cell.initialClassName) {
41356                 cell.initialClassName = cell.dom.className;
41357             }
41358             
41359             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41360         };
41361
41362         var i = 0;
41363         
41364         for(; i < startingPos; i++) {
41365             cells[i].dayName =  (++prevStart);
41366             Roo.log(textEls[i]);
41367             d.setDate(d.getDate()+1);
41368             
41369             //cells[i].className = "fc-past fc-other-month";
41370             setCellClass(this, cells[i]);
41371         }
41372         
41373         var intDay = 0;
41374         
41375         for(; i < days; i++){
41376             intDay = i - startingPos + 1;
41377             cells[i].dayName =  (intDay);
41378             d.setDate(d.getDate()+1);
41379             
41380             cells[i].className = ''; // "x-date-active";
41381             setCellClass(this, cells[i]);
41382         }
41383         var extraDays = 0;
41384         
41385         for(; i < 42; i++) {
41386             //textEls[i].innerHTML = (++extraDays);
41387             
41388             d.setDate(d.getDate()+1);
41389             cells[i].dayName = (++extraDays);
41390             cells[i].className = "fc-future fc-other-month";
41391             setCellClass(this, cells[i]);
41392         }
41393         
41394         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41395         
41396         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41397         
41398         // this will cause all the cells to mis
41399         var rows= [];
41400         var i =0;
41401         for (var r = 0;r < 6;r++) {
41402             for (var c =0;c < 7;c++) {
41403                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41404             }    
41405         }
41406         
41407         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41408         for(i=0;i<cells.length;i++) {
41409             
41410             this.cells.elements[i].dayName = cells[i].dayName ;
41411             this.cells.elements[i].className = cells[i].className;
41412             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41413             this.cells.elements[i].title = cells[i].title ;
41414             this.cells.elements[i].dateValue = cells[i].dateValue ;
41415         }
41416         
41417         
41418         
41419         
41420         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41421         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41422         
41423         ////if(totalRows != 6){
41424             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41425            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41426        // }
41427         
41428         this.fireEvent('monthchange', this, date);
41429         
41430         
41431     },
41432  /**
41433      * Returns the grid's SelectionModel.
41434      * @return {SelectionModel}
41435      */
41436     getSelectionModel : function(){
41437         if(!this.selModel){
41438             this.selModel = new Roo.grid.CellSelectionModel();
41439         }
41440         return this.selModel;
41441     },
41442
41443     load: function() {
41444         this.eventStore.load()
41445         
41446         
41447         
41448     },
41449     
41450     findCell : function(dt) {
41451         dt = dt.clearTime().getTime();
41452         var ret = false;
41453         this.cells.each(function(c){
41454             //Roo.log("check " +c.dateValue + '?=' + dt);
41455             if(c.dateValue == dt){
41456                 ret = c;
41457                 return false;
41458             }
41459             return true;
41460         });
41461         
41462         return ret;
41463     },
41464     
41465     findCells : function(rec) {
41466         var s = rec.data.start_dt.clone().clearTime().getTime();
41467        // Roo.log(s);
41468         var e= rec.data.end_dt.clone().clearTime().getTime();
41469        // Roo.log(e);
41470         var ret = [];
41471         this.cells.each(function(c){
41472              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41473             
41474             if(c.dateValue > e){
41475                 return ;
41476             }
41477             if(c.dateValue < s){
41478                 return ;
41479             }
41480             ret.push(c);
41481         });
41482         
41483         return ret;    
41484     },
41485     
41486     findBestRow: function(cells)
41487     {
41488         var ret = 0;
41489         
41490         for (var i =0 ; i < cells.length;i++) {
41491             ret  = Math.max(cells[i].rows || 0,ret);
41492         }
41493         return ret;
41494         
41495     },
41496     
41497     
41498     addItem : function(rec)
41499     {
41500         // look for vertical location slot in
41501         var cells = this.findCells(rec);
41502         
41503         rec.row = this.findBestRow(cells);
41504         
41505         // work out the location.
41506         
41507         var crow = false;
41508         var rows = [];
41509         for(var i =0; i < cells.length; i++) {
41510             if (!crow) {
41511                 crow = {
41512                     start : cells[i],
41513                     end :  cells[i]
41514                 };
41515                 continue;
41516             }
41517             if (crow.start.getY() == cells[i].getY()) {
41518                 // on same row.
41519                 crow.end = cells[i];
41520                 continue;
41521             }
41522             // different row.
41523             rows.push(crow);
41524             crow = {
41525                 start: cells[i],
41526                 end : cells[i]
41527             };
41528             
41529         }
41530         
41531         rows.push(crow);
41532         rec.els = [];
41533         rec.rows = rows;
41534         rec.cells = cells;
41535         for (var i = 0; i < cells.length;i++) {
41536             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41537             
41538         }
41539         
41540         
41541     },
41542     
41543     clearEvents: function() {
41544         
41545         if (!this.eventStore.getCount()) {
41546             return;
41547         }
41548         // reset number of rows in cells.
41549         Roo.each(this.cells.elements, function(c){
41550             c.rows = 0;
41551         });
41552         
41553         this.eventStore.each(function(e) {
41554             this.clearEvent(e);
41555         },this);
41556         
41557     },
41558     
41559     clearEvent : function(ev)
41560     {
41561         if (ev.els) {
41562             Roo.each(ev.els, function(el) {
41563                 el.un('mouseenter' ,this.onEventEnter, this);
41564                 el.un('mouseleave' ,this.onEventLeave, this);
41565                 el.remove();
41566             },this);
41567             ev.els = [];
41568         }
41569     },
41570     
41571     
41572     renderEvent : function(ev,ctr) {
41573         if (!ctr) {
41574              ctr = this.view.el.select('.fc-event-container',true).first();
41575         }
41576         
41577          
41578         this.clearEvent(ev);
41579             //code
41580        
41581         
41582         
41583         ev.els = [];
41584         var cells = ev.cells;
41585         var rows = ev.rows;
41586         this.fireEvent('eventrender', this, ev);
41587         
41588         for(var i =0; i < rows.length; i++) {
41589             
41590             cls = '';
41591             if (i == 0) {
41592                 cls += ' fc-event-start';
41593             }
41594             if ((i+1) == rows.length) {
41595                 cls += ' fc-event-end';
41596             }
41597             
41598             //Roo.log(ev.data);
41599             // how many rows should it span..
41600             var cg = this.eventTmpl.append(ctr,Roo.apply({
41601                 fccls : cls
41602                 
41603             }, ev.data) , true);
41604             
41605             
41606             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41607             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41608             cg.on('click', this.onEventClick, this, ev);
41609             
41610             ev.els.push(cg);
41611             
41612             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41613             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41614             //Roo.log(cg);
41615              
41616             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41617             cg.setWidth(ebox.right - sbox.x -2);
41618         }
41619     },
41620     
41621     renderEvents: function()
41622     {   
41623         // first make sure there is enough space..
41624         
41625         if (!this.eventTmpl) {
41626             this.eventTmpl = new Roo.Template(
41627                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41628                     '<div class="fc-event-inner">' +
41629                         '<span class="fc-event-time">{time}</span>' +
41630                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41631                     '</div>' +
41632                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41633                 '</div>'
41634             );
41635                 
41636         }
41637                
41638         
41639         
41640         this.cells.each(function(c) {
41641             //Roo.log(c.select('.fc-day-content div',true).first());
41642             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41643         });
41644         
41645         var ctr = this.view.el.select('.fc-event-container',true).first();
41646         
41647         var cls;
41648         this.eventStore.each(function(ev){
41649             
41650             this.renderEvent(ev);
41651              
41652              
41653         }, this);
41654         this.view.layout();
41655         
41656     },
41657     
41658     onEventEnter: function (e, el,event,d) {
41659         this.fireEvent('evententer', this, el, event);
41660     },
41661     
41662     onEventLeave: function (e, el,event,d) {
41663         this.fireEvent('eventleave', this, el, event);
41664     },
41665     
41666     onEventClick: function (e, el,event,d) {
41667         this.fireEvent('eventclick', this, el, event);
41668     },
41669     
41670     onMonthChange: function () {
41671         this.store.load();
41672     },
41673     
41674     onLoad: function () {
41675         
41676         //Roo.log('calendar onload');
41677 //         
41678         if(this.eventStore.getCount() > 0){
41679             
41680            
41681             
41682             this.eventStore.each(function(d){
41683                 
41684                 
41685                 // FIXME..
41686                 var add =   d.data;
41687                 if (typeof(add.end_dt) == 'undefined')  {
41688                     Roo.log("Missing End time in calendar data: ");
41689                     Roo.log(d);
41690                     return;
41691                 }
41692                 if (typeof(add.start_dt) == 'undefined')  {
41693                     Roo.log("Missing Start time in calendar data: ");
41694                     Roo.log(d);
41695                     return;
41696                 }
41697                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41698                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41699                 add.id = add.id || d.id;
41700                 add.title = add.title || '??';
41701                 
41702                 this.addItem(d);
41703                 
41704              
41705             },this);
41706         }
41707         
41708         this.renderEvents();
41709     }
41710     
41711
41712 });
41713 /*
41714  grid : {
41715                 xtype: 'Grid',
41716                 xns: Roo.grid,
41717                 listeners : {
41718                     render : function ()
41719                     {
41720                         _this.grid = this;
41721                         
41722                         if (!this.view.el.hasClass('course-timesheet')) {
41723                             this.view.el.addClass('course-timesheet');
41724                         }
41725                         if (this.tsStyle) {
41726                             this.ds.load({});
41727                             return; 
41728                         }
41729                         Roo.log('width');
41730                         Roo.log(_this.grid.view.el.getWidth());
41731                         
41732                         
41733                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41734                             '.course-timesheet .x-grid-row' : {
41735                                 height: '80px'
41736                             },
41737                             '.x-grid-row td' : {
41738                                 'vertical-align' : 0
41739                             },
41740                             '.course-edit-link' : {
41741                                 'color' : 'blue',
41742                                 'text-overflow' : 'ellipsis',
41743                                 'overflow' : 'hidden',
41744                                 'white-space' : 'nowrap',
41745                                 'cursor' : 'pointer'
41746                             },
41747                             '.sub-link' : {
41748                                 'color' : 'green'
41749                             },
41750                             '.de-act-sup-link' : {
41751                                 'color' : 'purple',
41752                                 'text-decoration' : 'line-through'
41753                             },
41754                             '.de-act-link' : {
41755                                 'color' : 'red',
41756                                 'text-decoration' : 'line-through'
41757                             },
41758                             '.course-timesheet .course-highlight' : {
41759                                 'border-top-style': 'dashed !important',
41760                                 'border-bottom-bottom': 'dashed !important'
41761                             },
41762                             '.course-timesheet .course-item' : {
41763                                 'font-family'   : 'tahoma, arial, helvetica',
41764                                 'font-size'     : '11px',
41765                                 'overflow'      : 'hidden',
41766                                 'padding-left'  : '10px',
41767                                 'padding-right' : '10px',
41768                                 'padding-top' : '10px' 
41769                             }
41770                             
41771                         }, Roo.id());
41772                                 this.ds.load({});
41773                     }
41774                 },
41775                 autoWidth : true,
41776                 monitorWindowResize : false,
41777                 cellrenderer : function(v,x,r)
41778                 {
41779                     return v;
41780                 },
41781                 sm : {
41782                     xtype: 'CellSelectionModel',
41783                     xns: Roo.grid
41784                 },
41785                 dataSource : {
41786                     xtype: 'Store',
41787                     xns: Roo.data,
41788                     listeners : {
41789                         beforeload : function (_self, options)
41790                         {
41791                             options.params = options.params || {};
41792                             options.params._month = _this.monthField.getValue();
41793                             options.params.limit = 9999;
41794                             options.params['sort'] = 'when_dt';    
41795                             options.params['dir'] = 'ASC';    
41796                             this.proxy.loadResponse = this.loadResponse;
41797                             Roo.log("load?");
41798                             //this.addColumns();
41799                         },
41800                         load : function (_self, records, options)
41801                         {
41802                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41803                                 // if you click on the translation.. you can edit it...
41804                                 var el = Roo.get(this);
41805                                 var id = el.dom.getAttribute('data-id');
41806                                 var d = el.dom.getAttribute('data-date');
41807                                 var t = el.dom.getAttribute('data-time');
41808                                 //var id = this.child('span').dom.textContent;
41809                                 
41810                                 //Roo.log(this);
41811                                 Pman.Dialog.CourseCalendar.show({
41812                                     id : id,
41813                                     when_d : d,
41814                                     when_t : t,
41815                                     productitem_active : id ? 1 : 0
41816                                 }, function() {
41817                                     _this.grid.ds.load({});
41818                                 });
41819                            
41820                            });
41821                            
41822                            _this.panel.fireEvent('resize', [ '', '' ]);
41823                         }
41824                     },
41825                     loadResponse : function(o, success, response){
41826                             // this is overridden on before load..
41827                             
41828                             Roo.log("our code?");       
41829                             //Roo.log(success);
41830                             //Roo.log(response)
41831                             delete this.activeRequest;
41832                             if(!success){
41833                                 this.fireEvent("loadexception", this, o, response);
41834                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41835                                 return;
41836                             }
41837                             var result;
41838                             try {
41839                                 result = o.reader.read(response);
41840                             }catch(e){
41841                                 Roo.log("load exception?");
41842                                 this.fireEvent("loadexception", this, o, response, e);
41843                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41844                                 return;
41845                             }
41846                             Roo.log("ready...");        
41847                             // loop through result.records;
41848                             // and set this.tdate[date] = [] << array of records..
41849                             _this.tdata  = {};
41850                             Roo.each(result.records, function(r){
41851                                 //Roo.log(r.data);
41852                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41853                                     _this.tdata[r.data.when_dt.format('j')] = [];
41854                                 }
41855                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41856                             });
41857                             
41858                             //Roo.log(_this.tdata);
41859                             
41860                             result.records = [];
41861                             result.totalRecords = 6;
41862                     
41863                             // let's generate some duumy records for the rows.
41864                             //var st = _this.dateField.getValue();
41865                             
41866                             // work out monday..
41867                             //st = st.add(Date.DAY, -1 * st.format('w'));
41868                             
41869                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41870                             
41871                             var firstOfMonth = date.getFirstDayOfMonth();
41872                             var days = date.getDaysInMonth();
41873                             var d = 1;
41874                             var firstAdded = false;
41875                             for (var i = 0; i < result.totalRecords ; i++) {
41876                                 //var d= st.add(Date.DAY, i);
41877                                 var row = {};
41878                                 var added = 0;
41879                                 for(var w = 0 ; w < 7 ; w++){
41880                                     if(!firstAdded && firstOfMonth != w){
41881                                         continue;
41882                                     }
41883                                     if(d > days){
41884                                         continue;
41885                                     }
41886                                     firstAdded = true;
41887                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41888                                     row['weekday'+w] = String.format(
41889                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41890                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41891                                                     d,
41892                                                     date.format('Y-m-')+dd
41893                                                 );
41894                                     added++;
41895                                     if(typeof(_this.tdata[d]) != 'undefined'){
41896                                         Roo.each(_this.tdata[d], function(r){
41897                                             var is_sub = '';
41898                                             var deactive = '';
41899                                             var id = r.id;
41900                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41901                                             if(r.parent_id*1>0){
41902                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41903                                                 id = r.parent_id;
41904                                             }
41905                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41906                                                 deactive = 'de-act-link';
41907                                             }
41908                                             
41909                                             row['weekday'+w] += String.format(
41910                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41911                                                     id, //0
41912                                                     r.product_id_name, //1
41913                                                     r.when_dt.format('h:ia'), //2
41914                                                     is_sub, //3
41915                                                     deactive, //4
41916                                                     desc // 5
41917                                             );
41918                                         });
41919                                     }
41920                                     d++;
41921                                 }
41922                                 
41923                                 // only do this if something added..
41924                                 if(added > 0){ 
41925                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41926                                 }
41927                                 
41928                                 
41929                                 // push it twice. (second one with an hour..
41930                                 
41931                             }
41932                             //Roo.log(result);
41933                             this.fireEvent("load", this, o, o.request.arg);
41934                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41935                         },
41936                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41937                     proxy : {
41938                         xtype: 'HttpProxy',
41939                         xns: Roo.data,
41940                         method : 'GET',
41941                         url : baseURL + '/Roo/Shop_course.php'
41942                     },
41943                     reader : {
41944                         xtype: 'JsonReader',
41945                         xns: Roo.data,
41946                         id : 'id',
41947                         fields : [
41948                             {
41949                                 'name': 'id',
41950                                 'type': 'int'
41951                             },
41952                             {
41953                                 'name': 'when_dt',
41954                                 'type': 'string'
41955                             },
41956                             {
41957                                 'name': 'end_dt',
41958                                 'type': 'string'
41959                             },
41960                             {
41961                                 'name': 'parent_id',
41962                                 'type': 'int'
41963                             },
41964                             {
41965                                 'name': 'product_id',
41966                                 'type': 'int'
41967                             },
41968                             {
41969                                 'name': 'productitem_id',
41970                                 'type': 'int'
41971                             },
41972                             {
41973                                 'name': 'guid',
41974                                 'type': 'int'
41975                             }
41976                         ]
41977                     }
41978                 },
41979                 toolbar : {
41980                     xtype: 'Toolbar',
41981                     xns: Roo,
41982                     items : [
41983                         {
41984                             xtype: 'Button',
41985                             xns: Roo.Toolbar,
41986                             listeners : {
41987                                 click : function (_self, e)
41988                                 {
41989                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41990                                     sd.setMonth(sd.getMonth()-1);
41991                                     _this.monthField.setValue(sd.format('Y-m-d'));
41992                                     _this.grid.ds.load({});
41993                                 }
41994                             },
41995                             text : "Back"
41996                         },
41997                         {
41998                             xtype: 'Separator',
41999                             xns: Roo.Toolbar
42000                         },
42001                         {
42002                             xtype: 'MonthField',
42003                             xns: Roo.form,
42004                             listeners : {
42005                                 render : function (_self)
42006                                 {
42007                                     _this.monthField = _self;
42008                                    // _this.monthField.set  today
42009                                 },
42010                                 select : function (combo, date)
42011                                 {
42012                                     _this.grid.ds.load({});
42013                                 }
42014                             },
42015                             value : (function() { return new Date(); })()
42016                         },
42017                         {
42018                             xtype: 'Separator',
42019                             xns: Roo.Toolbar
42020                         },
42021                         {
42022                             xtype: 'TextItem',
42023                             xns: Roo.Toolbar,
42024                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42025                         },
42026                         {
42027                             xtype: 'Fill',
42028                             xns: Roo.Toolbar
42029                         },
42030                         {
42031                             xtype: 'Button',
42032                             xns: Roo.Toolbar,
42033                             listeners : {
42034                                 click : function (_self, e)
42035                                 {
42036                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42037                                     sd.setMonth(sd.getMonth()+1);
42038                                     _this.monthField.setValue(sd.format('Y-m-d'));
42039                                     _this.grid.ds.load({});
42040                                 }
42041                             },
42042                             text : "Next"
42043                         }
42044                     ]
42045                 },
42046                  
42047             }
42048         };
42049         
42050         *//*
42051  * Based on:
42052  * Ext JS Library 1.1.1
42053  * Copyright(c) 2006-2007, Ext JS, LLC.
42054  *
42055  * Originally Released Under LGPL - original licence link has changed is not relivant.
42056  *
42057  * Fork - LGPL
42058  * <script type="text/javascript">
42059  */
42060  
42061 /**
42062  * @class Roo.LoadMask
42063  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42064  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42065  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42066  * element's UpdateManager load indicator and will be destroyed after the initial load.
42067  * @constructor
42068  * Create a new LoadMask
42069  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42070  * @param {Object} config The config object
42071  */
42072 Roo.LoadMask = function(el, config){
42073     this.el = Roo.get(el);
42074     Roo.apply(this, config);
42075     if(this.store){
42076         this.store.on('beforeload', this.onBeforeLoad, this);
42077         this.store.on('load', this.onLoad, this);
42078         this.store.on('loadexception', this.onLoadException, this);
42079         this.removeMask = false;
42080     }else{
42081         var um = this.el.getUpdateManager();
42082         um.showLoadIndicator = false; // disable the default indicator
42083         um.on('beforeupdate', this.onBeforeLoad, this);
42084         um.on('update', this.onLoad, this);
42085         um.on('failure', this.onLoad, this);
42086         this.removeMask = true;
42087     }
42088 };
42089
42090 Roo.LoadMask.prototype = {
42091     /**
42092      * @cfg {Boolean} removeMask
42093      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42094      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42095      */
42096     /**
42097      * @cfg {String} msg
42098      * The text to display in a centered loading message box (defaults to 'Loading...')
42099      */
42100     msg : 'Loading...',
42101     /**
42102      * @cfg {String} msgCls
42103      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42104      */
42105     msgCls : 'x-mask-loading',
42106
42107     /**
42108      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42109      * @type Boolean
42110      */
42111     disabled: false,
42112
42113     /**
42114      * Disables the mask to prevent it from being displayed
42115      */
42116     disable : function(){
42117        this.disabled = true;
42118     },
42119
42120     /**
42121      * Enables the mask so that it can be displayed
42122      */
42123     enable : function(){
42124         this.disabled = false;
42125     },
42126     
42127     onLoadException : function()
42128     {
42129         Roo.log(arguments);
42130         
42131         if (typeof(arguments[3]) != 'undefined') {
42132             Roo.MessageBox.alert("Error loading",arguments[3]);
42133         } 
42134         /*
42135         try {
42136             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42137                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42138             }   
42139         } catch(e) {
42140             
42141         }
42142         */
42143     
42144         
42145         
42146         this.el.unmask(this.removeMask);
42147     },
42148     // private
42149     onLoad : function()
42150     {
42151         this.el.unmask(this.removeMask);
42152     },
42153
42154     // private
42155     onBeforeLoad : function(){
42156         if(!this.disabled){
42157             this.el.mask(this.msg, this.msgCls);
42158         }
42159     },
42160
42161     // private
42162     destroy : function(){
42163         if(this.store){
42164             this.store.un('beforeload', this.onBeforeLoad, this);
42165             this.store.un('load', this.onLoad, this);
42166             this.store.un('loadexception', this.onLoadException, this);
42167         }else{
42168             var um = this.el.getUpdateManager();
42169             um.un('beforeupdate', this.onBeforeLoad, this);
42170             um.un('update', this.onLoad, this);
42171             um.un('failure', this.onLoad, this);
42172         }
42173     }
42174 };/*
42175  * Based on:
42176  * Ext JS Library 1.1.1
42177  * Copyright(c) 2006-2007, Ext JS, LLC.
42178  *
42179  * Originally Released Under LGPL - original licence link has changed is not relivant.
42180  *
42181  * Fork - LGPL
42182  * <script type="text/javascript">
42183  */
42184
42185
42186 /**
42187  * @class Roo.XTemplate
42188  * @extends Roo.Template
42189  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42190 <pre><code>
42191 var t = new Roo.XTemplate(
42192         '&lt;select name="{name}"&gt;',
42193                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42194         '&lt;/select&gt;'
42195 );
42196  
42197 // then append, applying the master template values
42198  </code></pre>
42199  *
42200  * Supported features:
42201  *
42202  *  Tags:
42203
42204 <pre><code>
42205       {a_variable} - output encoded.
42206       {a_variable.format:("Y-m-d")} - call a method on the variable
42207       {a_variable:raw} - unencoded output
42208       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42209       {a_variable:this.method_on_template(...)} - call a method on the template object.
42210  
42211 </code></pre>
42212  *  The tpl tag:
42213 <pre><code>
42214         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42215         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42216         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42217         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42218   
42219         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42220         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42221 </code></pre>
42222  *      
42223  */
42224 Roo.XTemplate = function()
42225 {
42226     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42227     if (this.html) {
42228         this.compile();
42229     }
42230 };
42231
42232
42233 Roo.extend(Roo.XTemplate, Roo.Template, {
42234
42235     /**
42236      * The various sub templates
42237      */
42238     tpls : false,
42239     /**
42240      *
42241      * basic tag replacing syntax
42242      * WORD:WORD()
42243      *
42244      * // you can fake an object call by doing this
42245      *  x.t:(test,tesT) 
42246      * 
42247      */
42248     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42249
42250     /**
42251      * compile the template
42252      *
42253      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42254      *
42255      */
42256     compile: function()
42257     {
42258         var s = this.html;
42259      
42260         s = ['<tpl>', s, '</tpl>'].join('');
42261     
42262         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42263             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42264             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42265             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42266             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42267             m,
42268             id     = 0,
42269             tpls   = [];
42270     
42271         while(true == !!(m = s.match(re))){
42272             var forMatch   = m[0].match(nameRe),
42273                 ifMatch   = m[0].match(ifRe),
42274                 execMatch   = m[0].match(execRe),
42275                 namedMatch   = m[0].match(namedRe),
42276                 
42277                 exp  = null, 
42278                 fn   = null,
42279                 exec = null,
42280                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42281                 
42282             if (ifMatch) {
42283                 // if - puts fn into test..
42284                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42285                 if(exp){
42286                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42287                 }
42288             }
42289             
42290             if (execMatch) {
42291                 // exec - calls a function... returns empty if true is  returned.
42292                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42293                 if(exp){
42294                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42295                 }
42296             }
42297             
42298             
42299             if (name) {
42300                 // for = 
42301                 switch(name){
42302                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42303                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42304                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42305                 }
42306             }
42307             var uid = namedMatch ? namedMatch[1] : id;
42308             
42309             
42310             tpls.push({
42311                 id:     namedMatch ? namedMatch[1] : id,
42312                 target: name,
42313                 exec:   exec,
42314                 test:   fn,
42315                 body:   m[1] || ''
42316             });
42317             if (namedMatch) {
42318                 s = s.replace(m[0], '');
42319             } else { 
42320                 s = s.replace(m[0], '{xtpl'+ id + '}');
42321             }
42322             ++id;
42323         }
42324         this.tpls = [];
42325         for(var i = tpls.length-1; i >= 0; --i){
42326             this.compileTpl(tpls[i]);
42327             this.tpls[tpls[i].id] = tpls[i];
42328         }
42329         this.master = tpls[tpls.length-1];
42330         return this;
42331     },
42332     /**
42333      * same as applyTemplate, except it's done to one of the subTemplates
42334      * when using named templates, you can do:
42335      *
42336      * var str = pl.applySubTemplate('your-name', values);
42337      *
42338      * 
42339      * @param {Number} id of the template
42340      * @param {Object} values to apply to template
42341      * @param {Object} parent (normaly the instance of this object)
42342      */
42343     applySubTemplate : function(id, values, parent)
42344     {
42345         
42346         
42347         var t = this.tpls[id];
42348         
42349         
42350         try { 
42351             if(t.test && !t.test.call(this, values, parent)){
42352                 return '';
42353             }
42354         } catch(e) {
42355             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42356             Roo.log(e.toString());
42357             Roo.log(t.test);
42358             return ''
42359         }
42360         try { 
42361             
42362             if(t.exec && t.exec.call(this, values, parent)){
42363                 return '';
42364             }
42365         } catch(e) {
42366             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42367             Roo.log(e.toString());
42368             Roo.log(t.exec);
42369             return ''
42370         }
42371         try {
42372             var vs = t.target ? t.target.call(this, values, parent) : values;
42373             parent = t.target ? values : parent;
42374             if(t.target && vs instanceof Array){
42375                 var buf = [];
42376                 for(var i = 0, len = vs.length; i < len; i++){
42377                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42378                 }
42379                 return buf.join('');
42380             }
42381             return t.compiled.call(this, vs, parent);
42382         } catch (e) {
42383             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42384             Roo.log(e.toString());
42385             Roo.log(t.compiled);
42386             return '';
42387         }
42388     },
42389
42390     compileTpl : function(tpl)
42391     {
42392         var fm = Roo.util.Format;
42393         var useF = this.disableFormats !== true;
42394         var sep = Roo.isGecko ? "+" : ",";
42395         var undef = function(str) {
42396             Roo.log("Property not found :"  + str);
42397             return '';
42398         };
42399         
42400         var fn = function(m, name, format, args)
42401         {
42402             //Roo.log(arguments);
42403             args = args ? args.replace(/\\'/g,"'") : args;
42404             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42405             if (typeof(format) == 'undefined') {
42406                 format= 'htmlEncode';
42407             }
42408             if (format == 'raw' ) {
42409                 format = false;
42410             }
42411             
42412             if(name.substr(0, 4) == 'xtpl'){
42413                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42414             }
42415             
42416             // build an array of options to determine if value is undefined..
42417             
42418             // basically get 'xxxx.yyyy' then do
42419             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42420             //    (function () { Roo.log("Property not found"); return ''; })() :
42421             //    ......
42422             
42423             var udef_ar = [];
42424             var lookfor = '';
42425             Roo.each(name.split('.'), function(st) {
42426                 lookfor += (lookfor.length ? '.': '') + st;
42427                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42428             });
42429             
42430             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42431             
42432             
42433             if(format && useF){
42434                 
42435                 args = args ? ',' + args : "";
42436                  
42437                 if(format.substr(0, 5) != "this."){
42438                     format = "fm." + format + '(';
42439                 }else{
42440                     format = 'this.call("'+ format.substr(5) + '", ';
42441                     args = ", values";
42442                 }
42443                 
42444                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42445             }
42446              
42447             if (args.length) {
42448                 // called with xxyx.yuu:(test,test)
42449                 // change to ()
42450                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42451             }
42452             // raw.. - :raw modifier..
42453             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42454             
42455         };
42456         var body;
42457         // branched to use + in gecko and [].join() in others
42458         if(Roo.isGecko){
42459             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42460                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42461                     "';};};";
42462         }else{
42463             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42464             body.push(tpl.body.replace(/(\r\n|\n)/g,
42465                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42466             body.push("'].join('');};};");
42467             body = body.join('');
42468         }
42469         
42470         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42471        
42472         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42473         eval(body);
42474         
42475         return this;
42476     },
42477
42478     applyTemplate : function(values){
42479         return this.master.compiled.call(this, values, {});
42480         //var s = this.subs;
42481     },
42482
42483     apply : function(){
42484         return this.applyTemplate.apply(this, arguments);
42485     }
42486
42487  });
42488
42489 Roo.XTemplate.from = function(el){
42490     el = Roo.getDom(el);
42491     return new Roo.XTemplate(el.value || el.innerHTML);
42492 };