roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) val = 0;
4462         return val;
4463     },
4464     
4465     /**
4466      * Integer sorting
4467      * @param {Mixed} s The value being converted
4468      * @return {Number} The comparison value
4469      */
4470     asInt : function(s) {
4471         var val = parseInt(String(s).replace(/,/g, ""));
4472         if(isNaN(val)) val = 0;
4473         return val;
4474     }
4475 };/*
4476  * Based on:
4477  * Ext JS Library 1.1.1
4478  * Copyright(c) 2006-2007, Ext JS, LLC.
4479  *
4480  * Originally Released Under LGPL - original licence link has changed is not relivant.
4481  *
4482  * Fork - LGPL
4483  * <script type="text/javascript">
4484  */
4485
4486 /**
4487 * @class Roo.data.Record
4488  * Instances of this class encapsulate both record <em>definition</em> information, and record
4489  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4490  * to access Records cached in an {@link Roo.data.Store} object.<br>
4491  * <p>
4492  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4493  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4494  * objects.<br>
4495  * <p>
4496  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4497  * @constructor
4498  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4499  * {@link #create}. The parameters are the same.
4500  * @param {Array} data An associative Array of data values keyed by the field name.
4501  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4502  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4503  * not specified an integer id is generated.
4504  */
4505 Roo.data.Record = function(data, id){
4506     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4507     this.data = data;
4508 };
4509
4510 /**
4511  * Generate a constructor for a specific record layout.
4512  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4513  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4514  * Each field definition object may contain the following properties: <ul>
4515  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4516  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4517  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4518  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4519  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4520  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4521  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4522  * this may be omitted.</p></li>
4523  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4524  * <ul><li>auto (Default, implies no conversion)</li>
4525  * <li>string</li>
4526  * <li>int</li>
4527  * <li>float</li>
4528  * <li>boolean</li>
4529  * <li>date</li></ul></p></li>
4530  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4531  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4532  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4533  * by the Reader into an object that will be stored in the Record. It is passed the
4534  * following parameters:<ul>
4535  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4536  * </ul></p></li>
4537  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4538  * </ul>
4539  * <br>usage:<br><pre><code>
4540 var TopicRecord = Roo.data.Record.create(
4541     {name: 'title', mapping: 'topic_title'},
4542     {name: 'author', mapping: 'username'},
4543     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4544     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4545     {name: 'lastPoster', mapping: 'user2'},
4546     {name: 'excerpt', mapping: 'post_text'}
4547 );
4548
4549 var myNewRecord = new TopicRecord({
4550     title: 'Do my job please',
4551     author: 'noobie',
4552     totalPosts: 1,
4553     lastPost: new Date(),
4554     lastPoster: 'Animal',
4555     excerpt: 'No way dude!'
4556 });
4557 myStore.add(myNewRecord);
4558 </code></pre>
4559  * @method create
4560  * @static
4561  */
4562 Roo.data.Record.create = function(o){
4563     var f = function(){
4564         f.superclass.constructor.apply(this, arguments);
4565     };
4566     Roo.extend(f, Roo.data.Record);
4567     var p = f.prototype;
4568     p.fields = new Roo.util.MixedCollection(false, function(field){
4569         return field.name;
4570     });
4571     for(var i = 0, len = o.length; i < len; i++){
4572         p.fields.add(new Roo.data.Field(o[i]));
4573     }
4574     f.getField = function(name){
4575         return p.fields.get(name);  
4576     };
4577     return f;
4578 };
4579
4580 Roo.data.Record.AUTO_ID = 1000;
4581 Roo.data.Record.EDIT = 'edit';
4582 Roo.data.Record.REJECT = 'reject';
4583 Roo.data.Record.COMMIT = 'commit';
4584
4585 Roo.data.Record.prototype = {
4586     /**
4587      * Readonly flag - true if this record has been modified.
4588      * @type Boolean
4589      */
4590     dirty : false,
4591     editing : false,
4592     error: null,
4593     modified: null,
4594
4595     // private
4596     join : function(store){
4597         this.store = store;
4598     },
4599
4600     /**
4601      * Set the named field to the specified value.
4602      * @param {String} name The name of the field to set.
4603      * @param {Object} value The value to set the field to.
4604      */
4605     set : function(name, value){
4606         if(this.data[name] == value){
4607             return;
4608         }
4609         this.dirty = true;
4610         if(!this.modified){
4611             this.modified = {};
4612         }
4613         if(typeof this.modified[name] == 'undefined'){
4614             this.modified[name] = this.data[name];
4615         }
4616         this.data[name] = value;
4617         if(!this.editing && this.store){
4618             this.store.afterEdit(this);
4619         }       
4620     },
4621
4622     /**
4623      * Get the value of the named field.
4624      * @param {String} name The name of the field to get the value of.
4625      * @return {Object} The value of the field.
4626      */
4627     get : function(name){
4628         return this.data[name]; 
4629     },
4630
4631     // private
4632     beginEdit : function(){
4633         this.editing = true;
4634         this.modified = {}; 
4635     },
4636
4637     // private
4638     cancelEdit : function(){
4639         this.editing = false;
4640         delete this.modified;
4641     },
4642
4643     // private
4644     endEdit : function(){
4645         this.editing = false;
4646         if(this.dirty && this.store){
4647             this.store.afterEdit(this);
4648         }
4649     },
4650
4651     /**
4652      * Usually called by the {@link Roo.data.Store} which owns the Record.
4653      * Rejects all changes made to the Record since either creation, or the last commit operation.
4654      * Modified fields are reverted to their original values.
4655      * <p>
4656      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4657      * of reject operations.
4658      */
4659     reject : function(){
4660         var m = this.modified;
4661         for(var n in m){
4662             if(typeof m[n] != "function"){
4663                 this.data[n] = m[n];
4664             }
4665         }
4666         this.dirty = false;
4667         delete this.modified;
4668         this.editing = false;
4669         if(this.store){
4670             this.store.afterReject(this);
4671         }
4672     },
4673
4674     /**
4675      * Usually called by the {@link Roo.data.Store} which owns the Record.
4676      * Commits all changes made to the Record since either creation, or the last commit operation.
4677      * <p>
4678      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4679      * of commit operations.
4680      */
4681     commit : function(){
4682         this.dirty = false;
4683         delete this.modified;
4684         this.editing = false;
4685         if(this.store){
4686             this.store.afterCommit(this);
4687         }
4688     },
4689
4690     // private
4691     hasError : function(){
4692         return this.error != null;
4693     },
4694
4695     // private
4696     clearError : function(){
4697         this.error = null;
4698     },
4699
4700     /**
4701      * Creates a copy of this record.
4702      * @param {String} id (optional) A new record id if you don't want to use this record's id
4703      * @return {Record}
4704      */
4705     copy : function(newId) {
4706         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4707     }
4708 };/*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718
4719
4720
4721 /**
4722  * @class Roo.data.Store
4723  * @extends Roo.util.Observable
4724  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4725  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4726  * <p>
4727  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4728  * has no knowledge of the format of the data returned by the Proxy.<br>
4729  * <p>
4730  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4731  * instances from the data object. These records are cached and made available through accessor functions.
4732  * @constructor
4733  * Creates a new Store.
4734  * @param {Object} config A config object containing the objects needed for the Store to access data,
4735  * and read the data into Records.
4736  */
4737 Roo.data.Store = function(config){
4738     this.data = new Roo.util.MixedCollection(false);
4739     this.data.getKey = function(o){
4740         return o.id;
4741     };
4742     this.baseParams = {};
4743     // private
4744     this.paramNames = {
4745         "start" : "start",
4746         "limit" : "limit",
4747         "sort" : "sort",
4748         "dir" : "dir",
4749         "multisort" : "_multisort"
4750     };
4751
4752     if(config && config.data){
4753         this.inlineData = config.data;
4754         delete config.data;
4755     }
4756
4757     Roo.apply(this, config);
4758     
4759     if(this.reader){ // reader passed
4760         this.reader = Roo.factory(this.reader, Roo.data);
4761         this.reader.xmodule = this.xmodule || false;
4762         if(!this.recordType){
4763             this.recordType = this.reader.recordType;
4764         }
4765         if(this.reader.onMetaChange){
4766             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4767         }
4768     }
4769
4770     if(this.recordType){
4771         this.fields = this.recordType.prototype.fields;
4772     }
4773     this.modified = [];
4774
4775     this.addEvents({
4776         /**
4777          * @event datachanged
4778          * Fires when the data cache has changed, and a widget which is using this Store
4779          * as a Record cache should refresh its view.
4780          * @param {Store} this
4781          */
4782         datachanged : true,
4783         /**
4784          * @event metachange
4785          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4786          * @param {Store} this
4787          * @param {Object} meta The JSON metadata
4788          */
4789         metachange : true,
4790         /**
4791          * @event add
4792          * Fires when Records have been added to the Store
4793          * @param {Store} this
4794          * @param {Roo.data.Record[]} records The array of Records added
4795          * @param {Number} index The index at which the record(s) were added
4796          */
4797         add : true,
4798         /**
4799          * @event remove
4800          * Fires when a Record has been removed from the Store
4801          * @param {Store} this
4802          * @param {Roo.data.Record} record The Record that was removed
4803          * @param {Number} index The index at which the record was removed
4804          */
4805         remove : true,
4806         /**
4807          * @event update
4808          * Fires when a Record has been updated
4809          * @param {Store} this
4810          * @param {Roo.data.Record} record The Record that was updated
4811          * @param {String} operation The update operation being performed.  Value may be one of:
4812          * <pre><code>
4813  Roo.data.Record.EDIT
4814  Roo.data.Record.REJECT
4815  Roo.data.Record.COMMIT
4816          * </code></pre>
4817          */
4818         update : true,
4819         /**
4820          * @event clear
4821          * Fires when the data cache has been cleared.
4822          * @param {Store} this
4823          */
4824         clear : true,
4825         /**
4826          * @event beforeload
4827          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4828          * the load action will be canceled.
4829          * @param {Store} this
4830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4831          */
4832         beforeload : true,
4833         /**
4834          * @event beforeloadadd
4835          * Fires after a new set of Records has been loaded.
4836          * @param {Store} this
4837          * @param {Roo.data.Record[]} records The Records that were loaded
4838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4839          */
4840         beforeloadadd : true,
4841         /**
4842          * @event load
4843          * Fires after a new set of Records has been loaded, before they are added to the store.
4844          * @param {Store} this
4845          * @param {Roo.data.Record[]} records The Records that were loaded
4846          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4847          * @params {Object} return from reader
4848          */
4849         load : true,
4850         /**
4851          * @event loadexception
4852          * Fires if an exception occurs in the Proxy during loading.
4853          * Called with the signature of the Proxy's "loadexception" event.
4854          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4855          * 
4856          * @param {Proxy} 
4857          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4858          * @param {Object} load options 
4859          * @param {Object} jsonData from your request (normally this contains the Exception)
4860          */
4861         loadexception : true
4862     });
4863     
4864     if(this.proxy){
4865         this.proxy = Roo.factory(this.proxy, Roo.data);
4866         this.proxy.xmodule = this.xmodule || false;
4867         this.relayEvents(this.proxy,  ["loadexception"]);
4868     }
4869     this.sortToggle = {};
4870     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4871
4872     Roo.data.Store.superclass.constructor.call(this);
4873
4874     if(this.inlineData){
4875         this.loadData(this.inlineData);
4876         delete this.inlineData;
4877     }
4878 };
4879
4880 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4881      /**
4882     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4883     * without a remote query - used by combo/forms at present.
4884     */
4885     
4886     /**
4887     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4888     */
4889     /**
4890     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4891     */
4892     /**
4893     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4894     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4895     */
4896     /**
4897     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4898     * on any HTTP request
4899     */
4900     /**
4901     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4902     */
4903     /**
4904     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4905     */
4906     multiSort: false,
4907     /**
4908     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4909     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4910     */
4911     remoteSort : false,
4912
4913     /**
4914     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4915      * loaded or when a record is removed. (defaults to false).
4916     */
4917     pruneModifiedRecords : false,
4918
4919     // private
4920     lastOptions : null,
4921
4922     /**
4923      * Add Records to the Store and fires the add event.
4924      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4925      */
4926     add : function(records){
4927         records = [].concat(records);
4928         for(var i = 0, len = records.length; i < len; i++){
4929             records[i].join(this);
4930         }
4931         var index = this.data.length;
4932         this.data.addAll(records);
4933         this.fireEvent("add", this, records, index);
4934     },
4935
4936     /**
4937      * Remove a Record from the Store and fires the remove event.
4938      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4939      */
4940     remove : function(record){
4941         var index = this.data.indexOf(record);
4942         this.data.removeAt(index);
4943         if(this.pruneModifiedRecords){
4944             this.modified.remove(record);
4945         }
4946         this.fireEvent("remove", this, record, index);
4947     },
4948
4949     /**
4950      * Remove all Records from the Store and fires the clear event.
4951      */
4952     removeAll : function(){
4953         this.data.clear();
4954         if(this.pruneModifiedRecords){
4955             this.modified = [];
4956         }
4957         this.fireEvent("clear", this);
4958     },
4959
4960     /**
4961      * Inserts Records to the Store at the given index and fires the add event.
4962      * @param {Number} index The start index at which to insert the passed Records.
4963      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4964      */
4965     insert : function(index, records){
4966         records = [].concat(records);
4967         for(var i = 0, len = records.length; i < len; i++){
4968             this.data.insert(index, records[i]);
4969             records[i].join(this);
4970         }
4971         this.fireEvent("add", this, records, index);
4972     },
4973
4974     /**
4975      * Get the index within the cache of the passed Record.
4976      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4977      * @return {Number} The index of the passed Record. Returns -1 if not found.
4978      */
4979     indexOf : function(record){
4980         return this.data.indexOf(record);
4981     },
4982
4983     /**
4984      * Get the index within the cache of the Record with the passed id.
4985      * @param {String} id The id of the Record to find.
4986      * @return {Number} The index of the Record. Returns -1 if not found.
4987      */
4988     indexOfId : function(id){
4989         return this.data.indexOfKey(id);
4990     },
4991
4992     /**
4993      * Get the Record with the specified id.
4994      * @param {String} id The id of the Record to find.
4995      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4996      */
4997     getById : function(id){
4998         return this.data.key(id);
4999     },
5000
5001     /**
5002      * Get the Record at the specified index.
5003      * @param {Number} index The index of the Record to find.
5004      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5005      */
5006     getAt : function(index){
5007         return this.data.itemAt(index);
5008     },
5009
5010     /**
5011      * Returns a range of Records between specified indices.
5012      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5013      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5014      * @return {Roo.data.Record[]} An array of Records
5015      */
5016     getRange : function(start, end){
5017         return this.data.getRange(start, end);
5018     },
5019
5020     // private
5021     storeOptions : function(o){
5022         o = Roo.apply({}, o);
5023         delete o.callback;
5024         delete o.scope;
5025         this.lastOptions = o;
5026     },
5027
5028     /**
5029      * Loads the Record cache from the configured Proxy using the configured Reader.
5030      * <p>
5031      * If using remote paging, then the first load call must specify the <em>start</em>
5032      * and <em>limit</em> properties in the options.params property to establish the initial
5033      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5034      * <p>
5035      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5036      * and this call will return before the new data has been loaded. Perform any post-processing
5037      * in a callback function, or in a "load" event handler.</strong>
5038      * <p>
5039      * @param {Object} options An object containing properties which control loading options:<ul>
5040      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5041      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5042      * passed the following arguments:<ul>
5043      * <li>r : Roo.data.Record[]</li>
5044      * <li>options: Options object from the load call</li>
5045      * <li>success: Boolean success indicator</li></ul></li>
5046      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5047      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5048      * </ul>
5049      */
5050     load : function(options){
5051         options = options || {};
5052         if(this.fireEvent("beforeload", this, options) !== false){
5053             this.storeOptions(options);
5054             var p = Roo.apply(options.params || {}, this.baseParams);
5055             // if meta was not loaded from remote source.. try requesting it.
5056             if (!this.reader.metaFromRemote) {
5057                 p._requestMeta = 1;
5058             }
5059             if(this.sortInfo && this.remoteSort){
5060                 var pn = this.paramNames;
5061                 p[pn["sort"]] = this.sortInfo.field;
5062                 p[pn["dir"]] = this.sortInfo.direction;
5063             }
5064             if (this.multiSort) {
5065                 var pn = this.paramNames;
5066                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5067             }
5068             
5069             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5070         }
5071     },
5072
5073     /**
5074      * Reloads the Record cache from the configured Proxy using the configured Reader and
5075      * the options from the last load operation performed.
5076      * @param {Object} options (optional) An object containing properties which may override the options
5077      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5078      * the most recently used options are reused).
5079      */
5080     reload : function(options){
5081         this.load(Roo.applyIf(options||{}, this.lastOptions));
5082     },
5083
5084     // private
5085     // Called as a callback by the Reader during a load operation.
5086     loadRecords : function(o, options, success){
5087         if(!o || success === false){
5088             if(success !== false){
5089                 this.fireEvent("load", this, [], options, o);
5090             }
5091             if(options.callback){
5092                 options.callback.call(options.scope || this, [], options, false);
5093             }
5094             return;
5095         }
5096         // if data returned failure - throw an exception.
5097         if (o.success === false) {
5098             // show a message if no listener is registered.
5099             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5100                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5101             }
5102             // loadmask wil be hooked into this..
5103             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5104             return;
5105         }
5106         var r = o.records, t = o.totalRecords || r.length;
5107         
5108         this.fireEvent("beforeloadadd", this, r, options, o);
5109         
5110         if(!options || options.add !== true){
5111             if(this.pruneModifiedRecords){
5112                 this.modified = [];
5113             }
5114             for(var i = 0, len = r.length; i < len; i++){
5115                 r[i].join(this);
5116             }
5117             if(this.snapshot){
5118                 this.data = this.snapshot;
5119                 delete this.snapshot;
5120             }
5121             this.data.clear();
5122             this.data.addAll(r);
5123             this.totalLength = t;
5124             this.applySort();
5125             this.fireEvent("datachanged", this);
5126         }else{
5127             this.totalLength = Math.max(t, this.data.length+r.length);
5128             this.add(r);
5129         }
5130         this.fireEvent("load", this, r, options, o);
5131         if(options.callback){
5132             options.callback.call(options.scope || this, r, options, true);
5133         }
5134     },
5135
5136
5137     /**
5138      * Loads data from a passed data block. A Reader which understands the format of the data
5139      * must have been configured in the constructor.
5140      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5141      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5142      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5143      */
5144     loadData : function(o, append){
5145         var r = this.reader.readRecords(o);
5146         this.loadRecords(r, {add: append}, true);
5147     },
5148
5149     /**
5150      * Gets the number of cached records.
5151      * <p>
5152      * <em>If using paging, this may not be the total size of the dataset. If the data object
5153      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5154      * the data set size</em>
5155      */
5156     getCount : function(){
5157         return this.data.length || 0;
5158     },
5159
5160     /**
5161      * Gets the total number of records in the dataset as returned by the server.
5162      * <p>
5163      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5164      * the dataset size</em>
5165      */
5166     getTotalCount : function(){
5167         return this.totalLength || 0;
5168     },
5169
5170     /**
5171      * Returns the sort state of the Store as an object with two properties:
5172      * <pre><code>
5173  field {String} The name of the field by which the Records are sorted
5174  direction {String} The sort order, "ASC" or "DESC"
5175      * </code></pre>
5176      */
5177     getSortState : function(){
5178         return this.sortInfo;
5179     },
5180
5181     // private
5182     applySort : function(){
5183         if(this.sortInfo && !this.remoteSort){
5184             var s = this.sortInfo, f = s.field;
5185             var st = this.fields.get(f).sortType;
5186             var fn = function(r1, r2){
5187                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5188                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5189             };
5190             this.data.sort(s.direction, fn);
5191             if(this.snapshot && this.snapshot != this.data){
5192                 this.snapshot.sort(s.direction, fn);
5193             }
5194         }
5195     },
5196
5197     /**
5198      * Sets the default sort column and order to be used by the next load operation.
5199      * @param {String} fieldName The name of the field to sort by.
5200      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5201      */
5202     setDefaultSort : function(field, dir){
5203         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5204     },
5205
5206     /**
5207      * Sort the Records.
5208      * If remote sorting is used, the sort is performed on the server, and the cache is
5209      * reloaded. If local sorting is used, the cache is sorted internally.
5210      * @param {String} fieldName The name of the field to sort by.
5211      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5212      */
5213     sort : function(fieldName, dir){
5214         var f = this.fields.get(fieldName);
5215         if(!dir){
5216             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5217             
5218             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5219                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5220             }else{
5221                 dir = f.sortDir;
5222             }
5223         }
5224         this.sortToggle[f.name] = dir;
5225         this.sortInfo = {field: f.name, direction: dir};
5226         if(!this.remoteSort){
5227             this.applySort();
5228             this.fireEvent("datachanged", this);
5229         }else{
5230             this.load(this.lastOptions);
5231         }
5232     },
5233
5234     /**
5235      * Calls the specified function for each of the Records in the cache.
5236      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5237      * Returning <em>false</em> aborts and exits the iteration.
5238      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5239      */
5240     each : function(fn, scope){
5241         this.data.each(fn, scope);
5242     },
5243
5244     /**
5245      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5246      * (e.g., during paging).
5247      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5248      */
5249     getModifiedRecords : function(){
5250         return this.modified;
5251     },
5252
5253     // private
5254     createFilterFn : function(property, value, anyMatch){
5255         if(!value.exec){ // not a regex
5256             value = String(value);
5257             if(value.length == 0){
5258                 return false;
5259             }
5260             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5261         }
5262         return function(r){
5263             return value.test(r.data[property]);
5264         };
5265     },
5266
5267     /**
5268      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5269      * @param {String} property A field on your records
5270      * @param {Number} start The record index to start at (defaults to 0)
5271      * @param {Number} end The last record index to include (defaults to length - 1)
5272      * @return {Number} The sum
5273      */
5274     sum : function(property, start, end){
5275         var rs = this.data.items, v = 0;
5276         start = start || 0;
5277         end = (end || end === 0) ? end : rs.length-1;
5278
5279         for(var i = start; i <= end; i++){
5280             v += (rs[i].data[property] || 0);
5281         }
5282         return v;
5283     },
5284
5285     /**
5286      * Filter the records by a specified property.
5287      * @param {String} field A field on your records
5288      * @param {String/RegExp} value Either a string that the field
5289      * should start with or a RegExp to test against the field
5290      * @param {Boolean} anyMatch True to match any part not just the beginning
5291      */
5292     filter : function(property, value, anyMatch){
5293         var fn = this.createFilterFn(property, value, anyMatch);
5294         return fn ? this.filterBy(fn) : this.clearFilter();
5295     },
5296
5297     /**
5298      * Filter by a function. The specified function will be called with each
5299      * record in this data source. If the function returns true the record is included,
5300      * otherwise it is filtered.
5301      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5302      * @param {Object} scope (optional) The scope of the function (defaults to this)
5303      */
5304     filterBy : function(fn, scope){
5305         this.snapshot = this.snapshot || this.data;
5306         this.data = this.queryBy(fn, scope||this);
5307         this.fireEvent("datachanged", this);
5308     },
5309
5310     /**
5311      * Query the records by a specified property.
5312      * @param {String} field A field on your records
5313      * @param {String/RegExp} value Either a string that the field
5314      * should start with or a RegExp to test against the field
5315      * @param {Boolean} anyMatch True to match any part not just the beginning
5316      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5317      */
5318     query : function(property, value, anyMatch){
5319         var fn = this.createFilterFn(property, value, anyMatch);
5320         return fn ? this.queryBy(fn) : this.data.clone();
5321     },
5322
5323     /**
5324      * Query by a function. The specified function will be called with each
5325      * record in this data source. If the function returns true the record is included
5326      * in the results.
5327      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5328      * @param {Object} scope (optional) The scope of the function (defaults to this)
5329       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5330      **/
5331     queryBy : function(fn, scope){
5332         var data = this.snapshot || this.data;
5333         return data.filterBy(fn, scope||this);
5334     },
5335
5336     /**
5337      * Collects unique values for a particular dataIndex from this store.
5338      * @param {String} dataIndex The property to collect
5339      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5340      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5341      * @return {Array} An array of the unique values
5342      **/
5343     collect : function(dataIndex, allowNull, bypassFilter){
5344         var d = (bypassFilter === true && this.snapshot) ?
5345                 this.snapshot.items : this.data.items;
5346         var v, sv, r = [], l = {};
5347         for(var i = 0, len = d.length; i < len; i++){
5348             v = d[i].data[dataIndex];
5349             sv = String(v);
5350             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5351                 l[sv] = true;
5352                 r[r.length] = v;
5353             }
5354         }
5355         return r;
5356     },
5357
5358     /**
5359      * Revert to a view of the Record cache with no filtering applied.
5360      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5361      */
5362     clearFilter : function(suppressEvent){
5363         if(this.snapshot && this.snapshot != this.data){
5364             this.data = this.snapshot;
5365             delete this.snapshot;
5366             if(suppressEvent !== true){
5367                 this.fireEvent("datachanged", this);
5368             }
5369         }
5370     },
5371
5372     // private
5373     afterEdit : function(record){
5374         if(this.modified.indexOf(record) == -1){
5375             this.modified.push(record);
5376         }
5377         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5378     },
5379     
5380     // private
5381     afterReject : function(record){
5382         this.modified.remove(record);
5383         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5384     },
5385
5386     // private
5387     afterCommit : function(record){
5388         this.modified.remove(record);
5389         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5390     },
5391
5392     /**
5393      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5394      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5395      */
5396     commitChanges : function(){
5397         var m = this.modified.slice(0);
5398         this.modified = [];
5399         for(var i = 0, len = m.length; i < len; i++){
5400             m[i].commit();
5401         }
5402     },
5403
5404     /**
5405      * Cancel outstanding changes on all changed records.
5406      */
5407     rejectChanges : function(){
5408         var m = this.modified.slice(0);
5409         this.modified = [];
5410         for(var i = 0, len = m.length; i < len; i++){
5411             m[i].reject();
5412         }
5413     },
5414
5415     onMetaChange : function(meta, rtype, o){
5416         this.recordType = rtype;
5417         this.fields = rtype.prototype.fields;
5418         delete this.snapshot;
5419         this.sortInfo = meta.sortInfo || this.sortInfo;
5420         this.modified = [];
5421         this.fireEvent('metachange', this, this.reader.meta);
5422     },
5423     
5424     moveIndex : function(data, type)
5425     {
5426         var index = this.indexOf(data);
5427         
5428         var newIndex = index + type;
5429         
5430         this.remove(data);
5431         
5432         this.insert(newIndex, data);
5433         
5434     }
5435 });/*
5436  * Based on:
5437  * Ext JS Library 1.1.1
5438  * Copyright(c) 2006-2007, Ext JS, LLC.
5439  *
5440  * Originally Released Under LGPL - original licence link has changed is not relivant.
5441  *
5442  * Fork - LGPL
5443  * <script type="text/javascript">
5444  */
5445
5446 /**
5447  * @class Roo.data.SimpleStore
5448  * @extends Roo.data.Store
5449  * Small helper class to make creating Stores from Array data easier.
5450  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5451  * @cfg {Array} fields An array of field definition objects, or field name strings.
5452  * @cfg {Array} data The multi-dimensional array of data
5453  * @constructor
5454  * @param {Object} config
5455  */
5456 Roo.data.SimpleStore = function(config){
5457     Roo.data.SimpleStore.superclass.constructor.call(this, {
5458         isLocal : true,
5459         reader: new Roo.data.ArrayReader({
5460                 id: config.id
5461             },
5462             Roo.data.Record.create(config.fields)
5463         ),
5464         proxy : new Roo.data.MemoryProxy(config.data)
5465     });
5466     this.load();
5467 };
5468 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478
5479 /**
5480 /**
5481  * @extends Roo.data.Store
5482  * @class Roo.data.JsonStore
5483  * Small helper class to make creating Stores for JSON data easier. <br/>
5484 <pre><code>
5485 var store = new Roo.data.JsonStore({
5486     url: 'get-images.php',
5487     root: 'images',
5488     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5489 });
5490 </code></pre>
5491  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5492  * JsonReader and HttpProxy (unless inline data is provided).</b>
5493  * @cfg {Array} fields An array of field definition objects, or field name strings.
5494  * @constructor
5495  * @param {Object} config
5496  */
5497 Roo.data.JsonStore = function(c){
5498     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5499         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5500         reader: new Roo.data.JsonReader(c, c.fields)
5501     }));
5502 };
5503 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514  
5515 Roo.data.Field = function(config){
5516     if(typeof config == "string"){
5517         config = {name: config};
5518     }
5519     Roo.apply(this, config);
5520     
5521     if(!this.type){
5522         this.type = "auto";
5523     }
5524     
5525     var st = Roo.data.SortTypes;
5526     // named sortTypes are supported, here we look them up
5527     if(typeof this.sortType == "string"){
5528         this.sortType = st[this.sortType];
5529     }
5530     
5531     // set default sortType for strings and dates
5532     if(!this.sortType){
5533         switch(this.type){
5534             case "string":
5535                 this.sortType = st.asUCString;
5536                 break;
5537             case "date":
5538                 this.sortType = st.asDate;
5539                 break;
5540             default:
5541                 this.sortType = st.none;
5542         }
5543     }
5544
5545     // define once
5546     var stripRe = /[\$,%]/g;
5547
5548     // prebuilt conversion function for this field, instead of
5549     // switching every time we're reading a value
5550     if(!this.convert){
5551         var cv, dateFormat = this.dateFormat;
5552         switch(this.type){
5553             case "":
5554             case "auto":
5555             case undefined:
5556                 cv = function(v){ return v; };
5557                 break;
5558             case "string":
5559                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5560                 break;
5561             case "int":
5562                 cv = function(v){
5563                     return v !== undefined && v !== null && v !== '' ?
5564                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5565                     };
5566                 break;
5567             case "float":
5568                 cv = function(v){
5569                     return v !== undefined && v !== null && v !== '' ?
5570                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5571                     };
5572                 break;
5573             case "bool":
5574             case "boolean":
5575                 cv = function(v){ return v === true || v === "true" || v == 1; };
5576                 break;
5577             case "date":
5578                 cv = function(v){
5579                     if(!v){
5580                         return '';
5581                     }
5582                     if(v instanceof Date){
5583                         return v;
5584                     }
5585                     if(dateFormat){
5586                         if(dateFormat == "timestamp"){
5587                             return new Date(v*1000);
5588                         }
5589                         return Date.parseDate(v, dateFormat);
5590                     }
5591                     var parsed = Date.parse(v);
5592                     return parsed ? new Date(parsed) : null;
5593                 };
5594              break;
5595             
5596         }
5597         this.convert = cv;
5598     }
5599 };
5600
5601 Roo.data.Field.prototype = {
5602     dateFormat: null,
5603     defaultValue: "",
5604     mapping: null,
5605     sortType : null,
5606     sortDir : "ASC"
5607 };/*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 // Base class for reading structured data from a data source.  This class is intended to be
5619 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5620
5621 /**
5622  * @class Roo.data.DataReader
5623  * Base class for reading structured data from a data source.  This class is intended to be
5624  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5625  */
5626
5627 Roo.data.DataReader = function(meta, recordType){
5628     
5629     this.meta = meta;
5630     
5631     this.recordType = recordType instanceof Array ? 
5632         Roo.data.Record.create(recordType) : recordType;
5633 };
5634
5635 Roo.data.DataReader.prototype = {
5636      /**
5637      * Create an empty record
5638      * @param {Object} data (optional) - overlay some values
5639      * @return {Roo.data.Record} record created.
5640      */
5641     newRow :  function(d) {
5642         var da =  {};
5643         this.recordType.prototype.fields.each(function(c) {
5644             switch( c.type) {
5645                 case 'int' : da[c.name] = 0; break;
5646                 case 'date' : da[c.name] = new Date(); break;
5647                 case 'float' : da[c.name] = 0.0; break;
5648                 case 'boolean' : da[c.name] = false; break;
5649                 default : da[c.name] = ""; break;
5650             }
5651             
5652         });
5653         return new this.recordType(Roo.apply(da, d));
5654     }
5655     
5656 };/*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.data.DataProxy
5669  * @extends Roo.data.Observable
5670  * This class is an abstract base class for implementations which provide retrieval of
5671  * unformatted data objects.<br>
5672  * <p>
5673  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5674  * (of the appropriate type which knows how to parse the data object) to provide a block of
5675  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5676  * <p>
5677  * Custom implementations must implement the load method as described in
5678  * {@link Roo.data.HttpProxy#load}.
5679  */
5680 Roo.data.DataProxy = function(){
5681     this.addEvents({
5682         /**
5683          * @event beforeload
5684          * Fires before a network request is made to retrieve a data object.
5685          * @param {Object} This DataProxy object.
5686          * @param {Object} params The params parameter to the load function.
5687          */
5688         beforeload : true,
5689         /**
5690          * @event load
5691          * Fires before the load method's callback is called.
5692          * @param {Object} This DataProxy object.
5693          * @param {Object} o The data object.
5694          * @param {Object} arg The callback argument object passed to the load function.
5695          */
5696         load : true,
5697         /**
5698          * @event loadexception
5699          * Fires if an Exception occurs during data retrieval.
5700          * @param {Object} This DataProxy object.
5701          * @param {Object} o The data object.
5702          * @param {Object} arg The callback argument object passed to the load function.
5703          * @param {Object} e The Exception.
5704          */
5705         loadexception : true
5706     });
5707     Roo.data.DataProxy.superclass.constructor.call(this);
5708 };
5709
5710 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5711
5712     /**
5713      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5714      */
5715 /*
5716  * Based on:
5717  * Ext JS Library 1.1.1
5718  * Copyright(c) 2006-2007, Ext JS, LLC.
5719  *
5720  * Originally Released Under LGPL - original licence link has changed is not relivant.
5721  *
5722  * Fork - LGPL
5723  * <script type="text/javascript">
5724  */
5725 /**
5726  * @class Roo.data.MemoryProxy
5727  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5728  * to the Reader when its load method is called.
5729  * @constructor
5730  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5731  */
5732 Roo.data.MemoryProxy = function(data){
5733     if (data.data) {
5734         data = data.data;
5735     }
5736     Roo.data.MemoryProxy.superclass.constructor.call(this);
5737     this.data = data;
5738 };
5739
5740 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5741     /**
5742      * Load data from the requested source (in this case an in-memory
5743      * data object passed to the constructor), read the data object into
5744      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5745      * process that block using the passed callback.
5746      * @param {Object} params This parameter is not used by the MemoryProxy class.
5747      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5748      * object into a block of Roo.data.Records.
5749      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5750      * The function must be passed <ul>
5751      * <li>The Record block object</li>
5752      * <li>The "arg" argument from the load function</li>
5753      * <li>A boolean success indicator</li>
5754      * </ul>
5755      * @param {Object} scope The scope in which to call the callback
5756      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5757      */
5758     load : function(params, reader, callback, scope, arg){
5759         params = params || {};
5760         var result;
5761         try {
5762             result = reader.readRecords(this.data);
5763         }catch(e){
5764             this.fireEvent("loadexception", this, arg, null, e);
5765             callback.call(scope, null, arg, false);
5766             return;
5767         }
5768         callback.call(scope, result, arg, true);
5769     },
5770     
5771     // private
5772     update : function(params, records){
5773         
5774     }
5775 });/*
5776  * Based on:
5777  * Ext JS Library 1.1.1
5778  * Copyright(c) 2006-2007, Ext JS, LLC.
5779  *
5780  * Originally Released Under LGPL - original licence link has changed is not relivant.
5781  *
5782  * Fork - LGPL
5783  * <script type="text/javascript">
5784  */
5785 /**
5786  * @class Roo.data.HttpProxy
5787  * @extends Roo.data.DataProxy
5788  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5789  * configured to reference a certain URL.<br><br>
5790  * <p>
5791  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5792  * from which the running page was served.<br><br>
5793  * <p>
5794  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5795  * <p>
5796  * Be aware that to enable the browser to parse an XML document, the server must set
5797  * the Content-Type header in the HTTP response to "text/xml".
5798  * @constructor
5799  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5800  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5801  * will be used to make the request.
5802  */
5803 Roo.data.HttpProxy = function(conn){
5804     Roo.data.HttpProxy.superclass.constructor.call(this);
5805     // is conn a conn config or a real conn?
5806     this.conn = conn;
5807     this.useAjax = !conn || !conn.events;
5808   
5809 };
5810
5811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5812     // thse are take from connection...
5813     
5814     /**
5815      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5816      */
5817     /**
5818      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5819      * extra parameters to each request made by this object. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5823      *  to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5827      */
5828     /**
5829      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5830      */
5831      /**
5832      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5833      * @type Boolean
5834      */
5835   
5836
5837     /**
5838      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5839      * @type Boolean
5840      */
5841     /**
5842      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5843      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5844      * a finer-grained basis than the DataProxy events.
5845      */
5846     getConnection : function(){
5847         return this.useAjax ? Roo.Ajax : this.conn;
5848     },
5849
5850     /**
5851      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5852      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5853      * process that block using the passed callback.
5854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5855      * for the request to the remote server.
5856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5857      * object into a block of Roo.data.Records.
5858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5859      * The function must be passed <ul>
5860      * <li>The Record block object</li>
5861      * <li>The "arg" argument from the load function</li>
5862      * <li>A boolean success indicator</li>
5863      * </ul>
5864      * @param {Object} scope The scope in which to call the callback
5865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5866      */
5867     load : function(params, reader, callback, scope, arg){
5868         if(this.fireEvent("beforeload", this, params) !== false){
5869             var  o = {
5870                 params : params || {},
5871                 request: {
5872                     callback : callback,
5873                     scope : scope,
5874                     arg : arg
5875                 },
5876                 reader: reader,
5877                 callback : this.loadResponse,
5878                 scope: this
5879             };
5880             if(this.useAjax){
5881                 Roo.applyIf(o, this.conn);
5882                 if(this.activeRequest){
5883                     Roo.Ajax.abort(this.activeRequest);
5884                 }
5885                 this.activeRequest = Roo.Ajax.request(o);
5886             }else{
5887                 this.conn.request(o);
5888             }
5889         }else{
5890             callback.call(scope||this, null, arg, false);
5891         }
5892     },
5893
5894     // private
5895     loadResponse : function(o, success, response){
5896         delete this.activeRequest;
5897         if(!success){
5898             this.fireEvent("loadexception", this, o, response);
5899             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5900             return;
5901         }
5902         var result;
5903         try {
5904             result = o.reader.read(response);
5905         }catch(e){
5906             this.fireEvent("loadexception", this, o, response, e);
5907             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5908             return;
5909         }
5910         
5911         this.fireEvent("load", this, o, o.request.arg);
5912         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5913     },
5914
5915     // private
5916     update : function(dataSet){
5917
5918     },
5919
5920     // private
5921     updateResponse : function(dataSet){
5922
5923     }
5924 });/*
5925  * Based on:
5926  * Ext JS Library 1.1.1
5927  * Copyright(c) 2006-2007, Ext JS, LLC.
5928  *
5929  * Originally Released Under LGPL - original licence link has changed is not relivant.
5930  *
5931  * Fork - LGPL
5932  * <script type="text/javascript">
5933  */
5934
5935 /**
5936  * @class Roo.data.ScriptTagProxy
5937  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5938  * other than the originating domain of the running page.<br><br>
5939  * <p>
5940  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5941  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5942  * <p>
5943  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5944  * source code that is used as the source inside a &lt;script> tag.<br><br>
5945  * <p>
5946  * In order for the browser to process the returned data, the server must wrap the data object
5947  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5948  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5949  * depending on whether the callback name was passed:
5950  * <p>
5951  * <pre><code>
5952 boolean scriptTag = false;
5953 String cb = request.getParameter("callback");
5954 if (cb != null) {
5955     scriptTag = true;
5956     response.setContentType("text/javascript");
5957 } else {
5958     response.setContentType("application/x-json");
5959 }
5960 Writer out = response.getWriter();
5961 if (scriptTag) {
5962     out.write(cb + "(");
5963 }
5964 out.print(dataBlock.toJsonString());
5965 if (scriptTag) {
5966     out.write(");");
5967 }
5968 </pre></code>
5969  *
5970  * @constructor
5971  * @param {Object} config A configuration object.
5972  */
5973 Roo.data.ScriptTagProxy = function(config){
5974     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5975     Roo.apply(this, config);
5976     this.head = document.getElementsByTagName("head")[0];
5977 };
5978
5979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5980
5981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5982     /**
5983      * @cfg {String} url The URL from which to request the data object.
5984      */
5985     /**
5986      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5987      */
5988     timeout : 30000,
5989     /**
5990      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5991      * the server the name of the callback function set up by the load call to process the returned data object.
5992      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5993      * javascript output which calls this named function passing the data object as its only parameter.
5994      */
5995     callbackParam : "callback",
5996     /**
5997      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5998      * name to the request.
5999      */
6000     nocache : true,
6001
6002     /**
6003      * Load data from the configured URL, read the data object into
6004      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6005      * process that block using the passed callback.
6006      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6007      * for the request to the remote server.
6008      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6009      * object into a block of Roo.data.Records.
6010      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6011      * The function must be passed <ul>
6012      * <li>The Record block object</li>
6013      * <li>The "arg" argument from the load function</li>
6014      * <li>A boolean success indicator</li>
6015      * </ul>
6016      * @param {Object} scope The scope in which to call the callback
6017      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6018      */
6019     load : function(params, reader, callback, scope, arg){
6020         if(this.fireEvent("beforeload", this, params) !== false){
6021
6022             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6023
6024             var url = this.url;
6025             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6026             if(this.nocache){
6027                 url += "&_dc=" + (new Date().getTime());
6028             }
6029             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6030             var trans = {
6031                 id : transId,
6032                 cb : "stcCallback"+transId,
6033                 scriptId : "stcScript"+transId,
6034                 params : params,
6035                 arg : arg,
6036                 url : url,
6037                 callback : callback,
6038                 scope : scope,
6039                 reader : reader
6040             };
6041             var conn = this;
6042
6043             window[trans.cb] = function(o){
6044                 conn.handleResponse(o, trans);
6045             };
6046
6047             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6048
6049             if(this.autoAbort !== false){
6050                 this.abort();
6051             }
6052
6053             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6054
6055             var script = document.createElement("script");
6056             script.setAttribute("src", url);
6057             script.setAttribute("type", "text/javascript");
6058             script.setAttribute("id", trans.scriptId);
6059             this.head.appendChild(script);
6060
6061             this.trans = trans;
6062         }else{
6063             callback.call(scope||this, null, arg, false);
6064         }
6065     },
6066
6067     // private
6068     isLoading : function(){
6069         return this.trans ? true : false;
6070     },
6071
6072     /**
6073      * Abort the current server request.
6074      */
6075     abort : function(){
6076         if(this.isLoading()){
6077             this.destroyTrans(this.trans);
6078         }
6079     },
6080
6081     // private
6082     destroyTrans : function(trans, isLoaded){
6083         this.head.removeChild(document.getElementById(trans.scriptId));
6084         clearTimeout(trans.timeoutId);
6085         if(isLoaded){
6086             window[trans.cb] = undefined;
6087             try{
6088                 delete window[trans.cb];
6089             }catch(e){}
6090         }else{
6091             // if hasn't been loaded, wait for load to remove it to prevent script error
6092             window[trans.cb] = function(){
6093                 window[trans.cb] = undefined;
6094                 try{
6095                     delete window[trans.cb];
6096                 }catch(e){}
6097             };
6098         }
6099     },
6100
6101     // private
6102     handleResponse : function(o, trans){
6103         this.trans = false;
6104         this.destroyTrans(trans, true);
6105         var result;
6106         try {
6107             result = trans.reader.readRecords(o);
6108         }catch(e){
6109             this.fireEvent("loadexception", this, o, trans.arg, e);
6110             trans.callback.call(trans.scope||window, null, trans.arg, false);
6111             return;
6112         }
6113         this.fireEvent("load", this, o, trans.arg);
6114         trans.callback.call(trans.scope||window, result, trans.arg, true);
6115     },
6116
6117     // private
6118     handleFailure : function(trans){
6119         this.trans = false;
6120         this.destroyTrans(trans, false);
6121         this.fireEvent("loadexception", this, null, trans.arg);
6122         trans.callback.call(trans.scope||window, null, trans.arg, false);
6123     }
6124 });/*
6125  * Based on:
6126  * Ext JS Library 1.1.1
6127  * Copyright(c) 2006-2007, Ext JS, LLC.
6128  *
6129  * Originally Released Under LGPL - original licence link has changed is not relivant.
6130  *
6131  * Fork - LGPL
6132  * <script type="text/javascript">
6133  */
6134
6135 /**
6136  * @class Roo.data.JsonReader
6137  * @extends Roo.data.DataReader
6138  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6139  * based on mappings in a provided Roo.data.Record constructor.
6140  * 
6141  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6142  * in the reply previously. 
6143  * 
6144  * <p>
6145  * Example code:
6146  * <pre><code>
6147 var RecordDef = Roo.data.Record.create([
6148     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6149     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6150 ]);
6151 var myReader = new Roo.data.JsonReader({
6152     totalProperty: "results",    // The property which contains the total dataset size (optional)
6153     root: "rows",                // The property which contains an Array of row objects
6154     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6155 }, RecordDef);
6156 </code></pre>
6157  * <p>
6158  * This would consume a JSON file like this:
6159  * <pre><code>
6160 { 'results': 2, 'rows': [
6161     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6162     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6163 }
6164 </code></pre>
6165  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6166  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6167  * paged from the remote server.
6168  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6169  * @cfg {String} root name of the property which contains the Array of row objects.
6170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6171  * @cfg {Array} fields Array of field definition objects
6172  * @constructor
6173  * Create a new JsonReader
6174  * @param {Object} meta Metadata configuration options
6175  * @param {Object} recordType Either an Array of field definition objects,
6176  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6177  */
6178 Roo.data.JsonReader = function(meta, recordType){
6179     
6180     meta = meta || {};
6181     // set some defaults:
6182     Roo.applyIf(meta, {
6183         totalProperty: 'total',
6184         successProperty : 'success',
6185         root : 'data',
6186         id : 'id'
6187     });
6188     
6189     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6190 };
6191 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6192     
6193     /**
6194      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6195      * Used by Store query builder to append _requestMeta to params.
6196      * 
6197      */
6198     metaFromRemote : false,
6199     /**
6200      * This method is only used by a DataProxy which has retrieved data from a remote server.
6201      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6202      * @return {Object} data A data block which is used by an Roo.data.Store object as
6203      * a cache of Roo.data.Records.
6204      */
6205     read : function(response){
6206         var json = response.responseText;
6207        
6208         var o = /* eval:var:o */ eval("("+json+")");
6209         if(!o) {
6210             throw {message: "JsonReader.read: Json object not found"};
6211         }
6212         
6213         if(o.metaData){
6214             
6215             delete this.ef;
6216             this.metaFromRemote = true;
6217             this.meta = o.metaData;
6218             this.recordType = Roo.data.Record.create(o.metaData.fields);
6219             this.onMetaChange(this.meta, this.recordType, o);
6220         }
6221         return this.readRecords(o);
6222     },
6223
6224     // private function a store will implement
6225     onMetaChange : function(meta, recordType, o){
6226
6227     },
6228
6229     /**
6230          * @ignore
6231          */
6232     simpleAccess: function(obj, subsc) {
6233         return obj[subsc];
6234     },
6235
6236         /**
6237          * @ignore
6238          */
6239     getJsonAccessor: function(){
6240         var re = /[\[\.]/;
6241         return function(expr) {
6242             try {
6243                 return(re.test(expr))
6244                     ? new Function("obj", "return obj." + expr)
6245                     : function(obj){
6246                         return obj[expr];
6247                     };
6248             } catch(e){}
6249             return Roo.emptyFn;
6250         };
6251     }(),
6252
6253     /**
6254      * Create a data block containing Roo.data.Records from an XML document.
6255      * @param {Object} o An object which contains an Array of row objects in the property specified
6256      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6257      * which contains the total size of the dataset.
6258      * @return {Object} data A data block which is used by an Roo.data.Store object as
6259      * a cache of Roo.data.Records.
6260      */
6261     readRecords : function(o){
6262         /**
6263          * After any data loads, the raw JSON data is available for further custom processing.
6264          * @type Object
6265          */
6266         this.o = o;
6267         var s = this.meta, Record = this.recordType,
6268             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6269
6270 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6271         if (!this.ef) {
6272             if(s.totalProperty) {
6273                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6274                 }
6275                 if(s.successProperty) {
6276                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6277                 }
6278                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6279                 if (s.id) {
6280                         var g = this.getJsonAccessor(s.id);
6281                         this.getId = function(rec) {
6282                                 var r = g(rec);  
6283                                 return (r === undefined || r === "") ? null : r;
6284                         };
6285                 } else {
6286                         this.getId = function(){return null;};
6287                 }
6288             this.ef = [];
6289             for(var jj = 0; jj < fl; jj++){
6290                 f = fi[jj];
6291                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6292                 this.ef[jj] = this.getJsonAccessor(map);
6293             }
6294         }
6295
6296         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6297         if(s.totalProperty){
6298             var vt = parseInt(this.getTotal(o), 10);
6299             if(!isNaN(vt)){
6300                 totalRecords = vt;
6301             }
6302         }
6303         if(s.successProperty){
6304             var vs = this.getSuccess(o);
6305             if(vs === false || vs === 'false'){
6306                 success = false;
6307             }
6308         }
6309         var records = [];
6310         for(var i = 0; i < c; i++){
6311                 var n = root[i];
6312             var values = {};
6313             var id = this.getId(n);
6314             for(var j = 0; j < fl; j++){
6315                 f = fi[j];
6316             var v = this.ef[j](n);
6317             if (!f.convert) {
6318                 Roo.log('missing convert for ' + f.name);
6319                 Roo.log(f);
6320                 continue;
6321             }
6322             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6323             }
6324             var record = new Record(values, id);
6325             record.json = n;
6326             records[i] = record;
6327         }
6328         return {
6329             raw : o,
6330             success : success,
6331             records : records,
6332             totalRecords : totalRecords
6333         };
6334     }
6335 });/*
6336  * Based on:
6337  * Ext JS Library 1.1.1
6338  * Copyright(c) 2006-2007, Ext JS, LLC.
6339  *
6340  * Originally Released Under LGPL - original licence link has changed is not relivant.
6341  *
6342  * Fork - LGPL
6343  * <script type="text/javascript">
6344  */
6345
6346 /**
6347  * @class Roo.data.XmlReader
6348  * @extends Roo.data.DataReader
6349  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6350  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6351  * <p>
6352  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6353  * header in the HTTP response must be set to "text/xml".</em>
6354  * <p>
6355  * Example code:
6356  * <pre><code>
6357 var RecordDef = Roo.data.Record.create([
6358    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6359    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6360 ]);
6361 var myReader = new Roo.data.XmlReader({
6362    totalRecords: "results", // The element which contains the total dataset size (optional)
6363    record: "row",           // The repeated element which contains row information
6364    id: "id"                 // The element within the row that provides an ID for the record (optional)
6365 }, RecordDef);
6366 </code></pre>
6367  * <p>
6368  * This would consume an XML file like this:
6369  * <pre><code>
6370 &lt;?xml?>
6371 &lt;dataset>
6372  &lt;results>2&lt;/results>
6373  &lt;row>
6374    &lt;id>1&lt;/id>
6375    &lt;name>Bill&lt;/name>
6376    &lt;occupation>Gardener&lt;/occupation>
6377  &lt;/row>
6378  &lt;row>
6379    &lt;id>2&lt;/id>
6380    &lt;name>Ben&lt;/name>
6381    &lt;occupation>Horticulturalist&lt;/occupation>
6382  &lt;/row>
6383 &lt;/dataset>
6384 </code></pre>
6385  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6386  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6387  * paged from the remote server.
6388  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6389  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6390  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6391  * a record identifier value.
6392  * @constructor
6393  * Create a new XmlReader
6394  * @param {Object} meta Metadata configuration options
6395  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6396  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6397  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6398  */
6399 Roo.data.XmlReader = function(meta, recordType){
6400     meta = meta || {};
6401     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6402 };
6403 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6404     /**
6405      * This method is only used by a DataProxy which has retrieved data from a remote server.
6406          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6407          * to contain a method called 'responseXML' that returns an XML document object.
6408      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6409      * a cache of Roo.data.Records.
6410      */
6411     read : function(response){
6412         var doc = response.responseXML;
6413         if(!doc) {
6414             throw {message: "XmlReader.read: XML Document not available"};
6415         }
6416         return this.readRecords(doc);
6417     },
6418
6419     /**
6420      * Create a data block containing Roo.data.Records from an XML document.
6421          * @param {Object} doc A parsed XML document.
6422      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6423      * a cache of Roo.data.Records.
6424      */
6425     readRecords : function(doc){
6426         /**
6427          * After any data loads/reads, the raw XML Document is available for further custom processing.
6428          * @type XMLDocument
6429          */
6430         this.xmlData = doc;
6431         var root = doc.documentElement || doc;
6432         var q = Roo.DomQuery;
6433         var recordType = this.recordType, fields = recordType.prototype.fields;
6434         var sid = this.meta.id;
6435         var totalRecords = 0, success = true;
6436         if(this.meta.totalRecords){
6437             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6438         }
6439         
6440         if(this.meta.success){
6441             var sv = q.selectValue(this.meta.success, root, true);
6442             success = sv !== false && sv !== 'false';
6443         }
6444         var records = [];
6445         var ns = q.select(this.meta.record, root);
6446         for(var i = 0, len = ns.length; i < len; i++) {
6447                 var n = ns[i];
6448                 var values = {};
6449                 var id = sid ? q.selectValue(sid, n) : undefined;
6450                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6451                     var f = fields.items[j];
6452                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6453                     v = f.convert(v);
6454                     values[f.name] = v;
6455                 }
6456                 var record = new recordType(values, id);
6457                 record.node = n;
6458                 records[records.length] = record;
6459             }
6460
6461             return {
6462                 success : success,
6463                 records : records,
6464                 totalRecords : totalRecords || records.length
6465             };
6466     }
6467 });/*
6468  * Based on:
6469  * Ext JS Library 1.1.1
6470  * Copyright(c) 2006-2007, Ext JS, LLC.
6471  *
6472  * Originally Released Under LGPL - original licence link has changed is not relivant.
6473  *
6474  * Fork - LGPL
6475  * <script type="text/javascript">
6476  */
6477
6478 /**
6479  * @class Roo.data.ArrayReader
6480  * @extends Roo.data.DataReader
6481  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6482  * Each element of that Array represents a row of data fields. The
6483  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6484  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6485  * <p>
6486  * Example code:.
6487  * <pre><code>
6488 var RecordDef = Roo.data.Record.create([
6489     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6490     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6491 ]);
6492 var myReader = new Roo.data.ArrayReader({
6493     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6494 }, RecordDef);
6495 </code></pre>
6496  * <p>
6497  * This would consume an Array like this:
6498  * <pre><code>
6499 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6500   </code></pre>
6501  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6502  * @constructor
6503  * Create a new JsonReader
6504  * @param {Object} meta Metadata configuration options.
6505  * @param {Object} recordType Either an Array of field definition objects
6506  * as specified to {@link Roo.data.Record#create},
6507  * or an {@link Roo.data.Record} object
6508  * created using {@link Roo.data.Record#create}.
6509  */
6510 Roo.data.ArrayReader = function(meta, recordType){
6511     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6512 };
6513
6514 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6515     /**
6516      * Create a data block containing Roo.data.Records from an XML document.
6517      * @param {Object} o An Array of row objects which represents the dataset.
6518      * @return {Object} data A data block which is used by an Roo.data.Store object as
6519      * a cache of Roo.data.Records.
6520      */
6521     readRecords : function(o){
6522         var sid = this.meta ? this.meta.id : null;
6523         var recordType = this.recordType, fields = recordType.prototype.fields;
6524         var records = [];
6525         var root = o;
6526             for(var i = 0; i < root.length; i++){
6527                     var n = root[i];
6528                 var values = {};
6529                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6530                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6531                 var f = fields.items[j];
6532                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6533                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6534                 v = f.convert(v);
6535                 values[f.name] = v;
6536             }
6537                 var record = new recordType(values, id);
6538                 record.json = n;
6539                 records[records.length] = record;
6540             }
6541             return {
6542                 records : records,
6543                 totalRecords : records.length
6544             };
6545     }
6546 });/*
6547  * Based on:
6548  * Ext JS Library 1.1.1
6549  * Copyright(c) 2006-2007, Ext JS, LLC.
6550  *
6551  * Originally Released Under LGPL - original licence link has changed is not relivant.
6552  *
6553  * Fork - LGPL
6554  * <script type="text/javascript">
6555  */
6556
6557
6558 /**
6559  * @class Roo.data.Tree
6560  * @extends Roo.util.Observable
6561  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6562  * in the tree have most standard DOM functionality.
6563  * @constructor
6564  * @param {Node} root (optional) The root node
6565  */
6566 Roo.data.Tree = function(root){
6567    this.nodeHash = {};
6568    /**
6569     * The root node for this tree
6570     * @type Node
6571     */
6572    this.root = null;
6573    if(root){
6574        this.setRootNode(root);
6575    }
6576    this.addEvents({
6577        /**
6578         * @event append
6579         * Fires when a new child node is appended to a node in this tree.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} parent The parent node
6582         * @param {Node} node The newly appended node
6583         * @param {Number} index The index of the newly appended node
6584         */
6585        "append" : true,
6586        /**
6587         * @event remove
6588         * Fires when a child node is removed from a node in this tree.
6589         * @param {Tree} tree The owner tree
6590         * @param {Node} parent The parent node
6591         * @param {Node} node The child node removed
6592         */
6593        "remove" : true,
6594        /**
6595         * @event move
6596         * Fires when a node is moved to a new location in the tree
6597         * @param {Tree} tree The owner tree
6598         * @param {Node} node The node moved
6599         * @param {Node} oldParent The old parent of this node
6600         * @param {Node} newParent The new parent of this node
6601         * @param {Number} index The index it was moved to
6602         */
6603        "move" : true,
6604        /**
6605         * @event insert
6606         * Fires when a new child node is inserted in a node in this tree.
6607         * @param {Tree} tree The owner tree
6608         * @param {Node} parent The parent node
6609         * @param {Node} node The child node inserted
6610         * @param {Node} refNode The child node the node was inserted before
6611         */
6612        "insert" : true,
6613        /**
6614         * @event beforeappend
6615         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6616         * @param {Tree} tree The owner tree
6617         * @param {Node} parent The parent node
6618         * @param {Node} node The child node to be appended
6619         */
6620        "beforeappend" : true,
6621        /**
6622         * @event beforeremove
6623         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6624         * @param {Tree} tree The owner tree
6625         * @param {Node} parent The parent node
6626         * @param {Node} node The child node to be removed
6627         */
6628        "beforeremove" : true,
6629        /**
6630         * @event beforemove
6631         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6632         * @param {Tree} tree The owner tree
6633         * @param {Node} node The node being moved
6634         * @param {Node} oldParent The parent of the node
6635         * @param {Node} newParent The new parent the node is moving to
6636         * @param {Number} index The index it is being moved to
6637         */
6638        "beforemove" : true,
6639        /**
6640         * @event beforeinsert
6641         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6642         * @param {Tree} tree The owner tree
6643         * @param {Node} parent The parent node
6644         * @param {Node} node The child node to be inserted
6645         * @param {Node} refNode The child node the node is being inserted before
6646         */
6647        "beforeinsert" : true
6648    });
6649
6650     Roo.data.Tree.superclass.constructor.call(this);
6651 };
6652
6653 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6654     pathSeparator: "/",
6655
6656     proxyNodeEvent : function(){
6657         return this.fireEvent.apply(this, arguments);
6658     },
6659
6660     /**
6661      * Returns the root node for this tree.
6662      * @return {Node}
6663      */
6664     getRootNode : function(){
6665         return this.root;
6666     },
6667
6668     /**
6669      * Sets the root node for this tree.
6670      * @param {Node} node
6671      * @return {Node}
6672      */
6673     setRootNode : function(node){
6674         this.root = node;
6675         node.ownerTree = this;
6676         node.isRoot = true;
6677         this.registerNode(node);
6678         return node;
6679     },
6680
6681     /**
6682      * Gets a node in this tree by its id.
6683      * @param {String} id
6684      * @return {Node}
6685      */
6686     getNodeById : function(id){
6687         return this.nodeHash[id];
6688     },
6689
6690     registerNode : function(node){
6691         this.nodeHash[node.id] = node;
6692     },
6693
6694     unregisterNode : function(node){
6695         delete this.nodeHash[node.id];
6696     },
6697
6698     toString : function(){
6699         return "[Tree"+(this.id?" "+this.id:"")+"]";
6700     }
6701 });
6702
6703 /**
6704  * @class Roo.data.Node
6705  * @extends Roo.util.Observable
6706  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6707  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6708  * @constructor
6709  * @param {Object} attributes The attributes/config for the node
6710  */
6711 Roo.data.Node = function(attributes){
6712     /**
6713      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6714      * @type {Object}
6715      */
6716     this.attributes = attributes || {};
6717     this.leaf = this.attributes.leaf;
6718     /**
6719      * The node id. @type String
6720      */
6721     this.id = this.attributes.id;
6722     if(!this.id){
6723         this.id = Roo.id(null, "ynode-");
6724         this.attributes.id = this.id;
6725     }
6726      
6727     
6728     /**
6729      * All child nodes of this node. @type Array
6730      */
6731     this.childNodes = [];
6732     if(!this.childNodes.indexOf){ // indexOf is a must
6733         this.childNodes.indexOf = function(o){
6734             for(var i = 0, len = this.length; i < len; i++){
6735                 if(this[i] == o) {
6736                     return i;
6737                 }
6738             }
6739             return -1;
6740         };
6741     }
6742     /**
6743      * The parent node for this node. @type Node
6744      */
6745     this.parentNode = null;
6746     /**
6747      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6748      */
6749     this.firstChild = null;
6750     /**
6751      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6752      */
6753     this.lastChild = null;
6754     /**
6755      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6756      */
6757     this.previousSibling = null;
6758     /**
6759      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6760      */
6761     this.nextSibling = null;
6762
6763     this.addEvents({
6764        /**
6765         * @event append
6766         * Fires when a new child node is appended
6767         * @param {Tree} tree The owner tree
6768         * @param {Node} this This node
6769         * @param {Node} node The newly appended node
6770         * @param {Number} index The index of the newly appended node
6771         */
6772        "append" : true,
6773        /**
6774         * @event remove
6775         * Fires when a child node is removed
6776         * @param {Tree} tree The owner tree
6777         * @param {Node} this This node
6778         * @param {Node} node The removed node
6779         */
6780        "remove" : true,
6781        /**
6782         * @event move
6783         * Fires when this node is moved to a new location in the tree
6784         * @param {Tree} tree The owner tree
6785         * @param {Node} this This node
6786         * @param {Node} oldParent The old parent of this node
6787         * @param {Node} newParent The new parent of this node
6788         * @param {Number} index The index it was moved to
6789         */
6790        "move" : true,
6791        /**
6792         * @event insert
6793         * Fires when a new child node is inserted.
6794         * @param {Tree} tree The owner tree
6795         * @param {Node} this This node
6796         * @param {Node} node The child node inserted
6797         * @param {Node} refNode The child node the node was inserted before
6798         */
6799        "insert" : true,
6800        /**
6801         * @event beforeappend
6802         * Fires before a new child is appended, return false to cancel the append.
6803         * @param {Tree} tree The owner tree
6804         * @param {Node} this This node
6805         * @param {Node} node The child node to be appended
6806         */
6807        "beforeappend" : true,
6808        /**
6809         * @event beforeremove
6810         * Fires before a child is removed, return false to cancel the remove.
6811         * @param {Tree} tree The owner tree
6812         * @param {Node} this This node
6813         * @param {Node} node The child node to be removed
6814         */
6815        "beforeremove" : true,
6816        /**
6817         * @event beforemove
6818         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6819         * @param {Tree} tree The owner tree
6820         * @param {Node} this This node
6821         * @param {Node} oldParent The parent of this node
6822         * @param {Node} newParent The new parent this node is moving to
6823         * @param {Number} index The index it is being moved to
6824         */
6825        "beforemove" : true,
6826        /**
6827         * @event beforeinsert
6828         * Fires before a new child is inserted, return false to cancel the insert.
6829         * @param {Tree} tree The owner tree
6830         * @param {Node} this This node
6831         * @param {Node} node The child node to be inserted
6832         * @param {Node} refNode The child node the node is being inserted before
6833         */
6834        "beforeinsert" : true
6835    });
6836     this.listeners = this.attributes.listeners;
6837     Roo.data.Node.superclass.constructor.call(this);
6838 };
6839
6840 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6841     fireEvent : function(evtName){
6842         // first do standard event for this node
6843         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6844             return false;
6845         }
6846         // then bubble it up to the tree if the event wasn't cancelled
6847         var ot = this.getOwnerTree();
6848         if(ot){
6849             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6850                 return false;
6851             }
6852         }
6853         return true;
6854     },
6855
6856     /**
6857      * Returns true if this node is a leaf
6858      * @return {Boolean}
6859      */
6860     isLeaf : function(){
6861         return this.leaf === true;
6862     },
6863
6864     // private
6865     setFirstChild : function(node){
6866         this.firstChild = node;
6867     },
6868
6869     //private
6870     setLastChild : function(node){
6871         this.lastChild = node;
6872     },
6873
6874
6875     /**
6876      * Returns true if this node is the last child of its parent
6877      * @return {Boolean}
6878      */
6879     isLast : function(){
6880        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6881     },
6882
6883     /**
6884      * Returns true if this node is the first child of its parent
6885      * @return {Boolean}
6886      */
6887     isFirst : function(){
6888        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6889     },
6890
6891     hasChildNodes : function(){
6892         return !this.isLeaf() && this.childNodes.length > 0;
6893     },
6894
6895     /**
6896      * Insert node(s) as the last child node of this node.
6897      * @param {Node/Array} node The node or Array of nodes to append
6898      * @return {Node} The appended node if single append, or null if an array was passed
6899      */
6900     appendChild : function(node){
6901         var multi = false;
6902         if(node instanceof Array){
6903             multi = node;
6904         }else if(arguments.length > 1){
6905             multi = arguments;
6906         }
6907         // if passed an array or multiple args do them one by one
6908         if(multi){
6909             for(var i = 0, len = multi.length; i < len; i++) {
6910                 this.appendChild(multi[i]);
6911             }
6912         }else{
6913             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6914                 return false;
6915             }
6916             var index = this.childNodes.length;
6917             var oldParent = node.parentNode;
6918             // it's a move, make sure we move it cleanly
6919             if(oldParent){
6920                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6921                     return false;
6922                 }
6923                 oldParent.removeChild(node);
6924             }
6925             index = this.childNodes.length;
6926             if(index == 0){
6927                 this.setFirstChild(node);
6928             }
6929             this.childNodes.push(node);
6930             node.parentNode = this;
6931             var ps = this.childNodes[index-1];
6932             if(ps){
6933                 node.previousSibling = ps;
6934                 ps.nextSibling = node;
6935             }else{
6936                 node.previousSibling = null;
6937             }
6938             node.nextSibling = null;
6939             this.setLastChild(node);
6940             node.setOwnerTree(this.getOwnerTree());
6941             this.fireEvent("append", this.ownerTree, this, node, index);
6942             if(oldParent){
6943                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6944             }
6945             return node;
6946         }
6947     },
6948
6949     /**
6950      * Removes a child node from this node.
6951      * @param {Node} node The node to remove
6952      * @return {Node} The removed node
6953      */
6954     removeChild : function(node){
6955         var index = this.childNodes.indexOf(node);
6956         if(index == -1){
6957             return false;
6958         }
6959         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6960             return false;
6961         }
6962
6963         // remove it from childNodes collection
6964         this.childNodes.splice(index, 1);
6965
6966         // update siblings
6967         if(node.previousSibling){
6968             node.previousSibling.nextSibling = node.nextSibling;
6969         }
6970         if(node.nextSibling){
6971             node.nextSibling.previousSibling = node.previousSibling;
6972         }
6973
6974         // update child refs
6975         if(this.firstChild == node){
6976             this.setFirstChild(node.nextSibling);
6977         }
6978         if(this.lastChild == node){
6979             this.setLastChild(node.previousSibling);
6980         }
6981
6982         node.setOwnerTree(null);
6983         // clear any references from the node
6984         node.parentNode = null;
6985         node.previousSibling = null;
6986         node.nextSibling = null;
6987         this.fireEvent("remove", this.ownerTree, this, node);
6988         return node;
6989     },
6990
6991     /**
6992      * Inserts the first node before the second node in this nodes childNodes collection.
6993      * @param {Node} node The node to insert
6994      * @param {Node} refNode The node to insert before (if null the node is appended)
6995      * @return {Node} The inserted node
6996      */
6997     insertBefore : function(node, refNode){
6998         if(!refNode){ // like standard Dom, refNode can be null for append
6999             return this.appendChild(node);
7000         }
7001         // nothing to do
7002         if(node == refNode){
7003             return false;
7004         }
7005
7006         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7007             return false;
7008         }
7009         var index = this.childNodes.indexOf(refNode);
7010         var oldParent = node.parentNode;
7011         var refIndex = index;
7012
7013         // when moving internally, indexes will change after remove
7014         if(oldParent == this && this.childNodes.indexOf(node) < index){
7015             refIndex--;
7016         }
7017
7018         // it's a move, make sure we move it cleanly
7019         if(oldParent){
7020             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7021                 return false;
7022             }
7023             oldParent.removeChild(node);
7024         }
7025         if(refIndex == 0){
7026             this.setFirstChild(node);
7027         }
7028         this.childNodes.splice(refIndex, 0, node);
7029         node.parentNode = this;
7030         var ps = this.childNodes[refIndex-1];
7031         if(ps){
7032             node.previousSibling = ps;
7033             ps.nextSibling = node;
7034         }else{
7035             node.previousSibling = null;
7036         }
7037         node.nextSibling = refNode;
7038         refNode.previousSibling = node;
7039         node.setOwnerTree(this.getOwnerTree());
7040         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7041         if(oldParent){
7042             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7043         }
7044         return node;
7045     },
7046
7047     /**
7048      * Returns the child node at the specified index.
7049      * @param {Number} index
7050      * @return {Node}
7051      */
7052     item : function(index){
7053         return this.childNodes[index];
7054     },
7055
7056     /**
7057      * Replaces one child node in this node with another.
7058      * @param {Node} newChild The replacement node
7059      * @param {Node} oldChild The node to replace
7060      * @return {Node} The replaced node
7061      */
7062     replaceChild : function(newChild, oldChild){
7063         this.insertBefore(newChild, oldChild);
7064         this.removeChild(oldChild);
7065         return oldChild;
7066     },
7067
7068     /**
7069      * Returns the index of a child node
7070      * @param {Node} node
7071      * @return {Number} The index of the node or -1 if it was not found
7072      */
7073     indexOf : function(child){
7074         return this.childNodes.indexOf(child);
7075     },
7076
7077     /**
7078      * Returns the tree this node is in.
7079      * @return {Tree}
7080      */
7081     getOwnerTree : function(){
7082         // if it doesn't have one, look for one
7083         if(!this.ownerTree){
7084             var p = this;
7085             while(p){
7086                 if(p.ownerTree){
7087                     this.ownerTree = p.ownerTree;
7088                     break;
7089                 }
7090                 p = p.parentNode;
7091             }
7092         }
7093         return this.ownerTree;
7094     },
7095
7096     /**
7097      * Returns depth of this node (the root node has a depth of 0)
7098      * @return {Number}
7099      */
7100     getDepth : function(){
7101         var depth = 0;
7102         var p = this;
7103         while(p.parentNode){
7104             ++depth;
7105             p = p.parentNode;
7106         }
7107         return depth;
7108     },
7109
7110     // private
7111     setOwnerTree : function(tree){
7112         // if it's move, we need to update everyone
7113         if(tree != this.ownerTree){
7114             if(this.ownerTree){
7115                 this.ownerTree.unregisterNode(this);
7116             }
7117             this.ownerTree = tree;
7118             var cs = this.childNodes;
7119             for(var i = 0, len = cs.length; i < len; i++) {
7120                 cs[i].setOwnerTree(tree);
7121             }
7122             if(tree){
7123                 tree.registerNode(this);
7124             }
7125         }
7126     },
7127
7128     /**
7129      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7130      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7131      * @return {String} The path
7132      */
7133     getPath : function(attr){
7134         attr = attr || "id";
7135         var p = this.parentNode;
7136         var b = [this.attributes[attr]];
7137         while(p){
7138             b.unshift(p.attributes[attr]);
7139             p = p.parentNode;
7140         }
7141         var sep = this.getOwnerTree().pathSeparator;
7142         return sep + b.join(sep);
7143     },
7144
7145     /**
7146      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7147      * function call will be the scope provided or the current node. The arguments to the function
7148      * will be the args provided or the current node. If the function returns false at any point,
7149      * the bubble is stopped.
7150      * @param {Function} fn The function to call
7151      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7152      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7153      */
7154     bubble : function(fn, scope, args){
7155         var p = this;
7156         while(p){
7157             if(fn.call(scope || p, args || p) === false){
7158                 break;
7159             }
7160             p = p.parentNode;
7161         }
7162     },
7163
7164     /**
7165      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7166      * function call will be the scope provided or the current node. The arguments to the function
7167      * will be the args provided or the current node. If the function returns false at any point,
7168      * the cascade is stopped on that branch.
7169      * @param {Function} fn The function to call
7170      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7171      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7172      */
7173     cascade : function(fn, scope, args){
7174         if(fn.call(scope || this, args || this) !== false){
7175             var cs = this.childNodes;
7176             for(var i = 0, len = cs.length; i < len; i++) {
7177                 cs[i].cascade(fn, scope, args);
7178             }
7179         }
7180     },
7181
7182     /**
7183      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7184      * function call will be the scope provided or the current node. The arguments to the function
7185      * will be the args provided or the current node. If the function returns false at any point,
7186      * the iteration stops.
7187      * @param {Function} fn The function to call
7188      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7189      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7190      */
7191     eachChild : function(fn, scope, args){
7192         var cs = this.childNodes;
7193         for(var i = 0, len = cs.length; i < len; i++) {
7194                 if(fn.call(scope || this, args || cs[i]) === false){
7195                     break;
7196                 }
7197         }
7198     },
7199
7200     /**
7201      * Finds the first child that has the attribute with the specified value.
7202      * @param {String} attribute The attribute name
7203      * @param {Mixed} value The value to search for
7204      * @return {Node} The found child or null if none was found
7205      */
7206     findChild : function(attribute, value){
7207         var cs = this.childNodes;
7208         for(var i = 0, len = cs.length; i < len; i++) {
7209                 if(cs[i].attributes[attribute] == value){
7210                     return cs[i];
7211                 }
7212         }
7213         return null;
7214     },
7215
7216     /**
7217      * Finds the first child by a custom function. The child matches if the function passed
7218      * returns true.
7219      * @param {Function} fn
7220      * @param {Object} scope (optional)
7221      * @return {Node} The found child or null if none was found
7222      */
7223     findChildBy : function(fn, scope){
7224         var cs = this.childNodes;
7225         for(var i = 0, len = cs.length; i < len; i++) {
7226                 if(fn.call(scope||cs[i], cs[i]) === true){
7227                     return cs[i];
7228                 }
7229         }
7230         return null;
7231     },
7232
7233     /**
7234      * Sorts this nodes children using the supplied sort function
7235      * @param {Function} fn
7236      * @param {Object} scope (optional)
7237      */
7238     sort : function(fn, scope){
7239         var cs = this.childNodes;
7240         var len = cs.length;
7241         if(len > 0){
7242             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7243             cs.sort(sortFn);
7244             for(var i = 0; i < len; i++){
7245                 var n = cs[i];
7246                 n.previousSibling = cs[i-1];
7247                 n.nextSibling = cs[i+1];
7248                 if(i == 0){
7249                     this.setFirstChild(n);
7250                 }
7251                 if(i == len-1){
7252                     this.setLastChild(n);
7253                 }
7254             }
7255         }
7256     },
7257
7258     /**
7259      * Returns true if this node is an ancestor (at any point) of the passed node.
7260      * @param {Node} node
7261      * @return {Boolean}
7262      */
7263     contains : function(node){
7264         return node.isAncestor(this);
7265     },
7266
7267     /**
7268      * Returns true if the passed node is an ancestor (at any point) of this node.
7269      * @param {Node} node
7270      * @return {Boolean}
7271      */
7272     isAncestor : function(node){
7273         var p = this.parentNode;
7274         while(p){
7275             if(p == node){
7276                 return true;
7277             }
7278             p = p.parentNode;
7279         }
7280         return false;
7281     },
7282
7283     toString : function(){
7284         return "[Node"+(this.id?" "+this.id:"")+"]";
7285     }
7286 });/*
7287  * Based on:
7288  * Ext JS Library 1.1.1
7289  * Copyright(c) 2006-2007, Ext JS, LLC.
7290  *
7291  * Originally Released Under LGPL - original licence link has changed is not relivant.
7292  *
7293  * Fork - LGPL
7294  * <script type="text/javascript">
7295  */
7296  (function(){ 
7297 /**
7298  * @class Roo.Layer
7299  * @extends Roo.Element
7300  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7301  * automatic maintaining of shadow/shim positions.
7302  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7303  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7304  * you can pass a string with a CSS class name. False turns off the shadow.
7305  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7306  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7307  * @cfg {String} cls CSS class to add to the element
7308  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7309  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7310  * @constructor
7311  * @param {Object} config An object with config options.
7312  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7313  */
7314
7315 Roo.Layer = function(config, existingEl){
7316     config = config || {};
7317     var dh = Roo.DomHelper;
7318     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7319     if(existingEl){
7320         this.dom = Roo.getDom(existingEl);
7321     }
7322     if(!this.dom){
7323         var o = config.dh || {tag: "div", cls: "x-layer"};
7324         this.dom = dh.append(pel, o);
7325     }
7326     if(config.cls){
7327         this.addClass(config.cls);
7328     }
7329     this.constrain = config.constrain !== false;
7330     this.visibilityMode = Roo.Element.VISIBILITY;
7331     if(config.id){
7332         this.id = this.dom.id = config.id;
7333     }else{
7334         this.id = Roo.id(this.dom);
7335     }
7336     this.zindex = config.zindex || this.getZIndex();
7337     this.position("absolute", this.zindex);
7338     if(config.shadow){
7339         this.shadowOffset = config.shadowOffset || 4;
7340         this.shadow = new Roo.Shadow({
7341             offset : this.shadowOffset,
7342             mode : config.shadow
7343         });
7344     }else{
7345         this.shadowOffset = 0;
7346     }
7347     this.useShim = config.shim !== false && Roo.useShims;
7348     this.useDisplay = config.useDisplay;
7349     this.hide();
7350 };
7351
7352 var supr = Roo.Element.prototype;
7353
7354 // shims are shared among layer to keep from having 100 iframes
7355 var shims = [];
7356
7357 Roo.extend(Roo.Layer, Roo.Element, {
7358
7359     getZIndex : function(){
7360         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7361     },
7362
7363     getShim : function(){
7364         if(!this.useShim){
7365             return null;
7366         }
7367         if(this.shim){
7368             return this.shim;
7369         }
7370         var shim = shims.shift();
7371         if(!shim){
7372             shim = this.createShim();
7373             shim.enableDisplayMode('block');
7374             shim.dom.style.display = 'none';
7375             shim.dom.style.visibility = 'visible';
7376         }
7377         var pn = this.dom.parentNode;
7378         if(shim.dom.parentNode != pn){
7379             pn.insertBefore(shim.dom, this.dom);
7380         }
7381         shim.setStyle('z-index', this.getZIndex()-2);
7382         this.shim = shim;
7383         return shim;
7384     },
7385
7386     hideShim : function(){
7387         if(this.shim){
7388             this.shim.setDisplayed(false);
7389             shims.push(this.shim);
7390             delete this.shim;
7391         }
7392     },
7393
7394     disableShadow : function(){
7395         if(this.shadow){
7396             this.shadowDisabled = true;
7397             this.shadow.hide();
7398             this.lastShadowOffset = this.shadowOffset;
7399             this.shadowOffset = 0;
7400         }
7401     },
7402
7403     enableShadow : function(show){
7404         if(this.shadow){
7405             this.shadowDisabled = false;
7406             this.shadowOffset = this.lastShadowOffset;
7407             delete this.lastShadowOffset;
7408             if(show){
7409                 this.sync(true);
7410             }
7411         }
7412     },
7413
7414     // private
7415     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7416     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7417     sync : function(doShow){
7418         var sw = this.shadow;
7419         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7420             var sh = this.getShim();
7421
7422             var w = this.getWidth(),
7423                 h = this.getHeight();
7424
7425             var l = this.getLeft(true),
7426                 t = this.getTop(true);
7427
7428             if(sw && !this.shadowDisabled){
7429                 if(doShow && !sw.isVisible()){
7430                     sw.show(this);
7431                 }else{
7432                     sw.realign(l, t, w, h);
7433                 }
7434                 if(sh){
7435                     if(doShow){
7436                        sh.show();
7437                     }
7438                     // fit the shim behind the shadow, so it is shimmed too
7439                     var a = sw.adjusts, s = sh.dom.style;
7440                     s.left = (Math.min(l, l+a.l))+"px";
7441                     s.top = (Math.min(t, t+a.t))+"px";
7442                     s.width = (w+a.w)+"px";
7443                     s.height = (h+a.h)+"px";
7444                 }
7445             }else if(sh){
7446                 if(doShow){
7447                    sh.show();
7448                 }
7449                 sh.setSize(w, h);
7450                 sh.setLeftTop(l, t);
7451             }
7452             
7453         }
7454     },
7455
7456     // private
7457     destroy : function(){
7458         this.hideShim();
7459         if(this.shadow){
7460             this.shadow.hide();
7461         }
7462         this.removeAllListeners();
7463         var pn = this.dom.parentNode;
7464         if(pn){
7465             pn.removeChild(this.dom);
7466         }
7467         Roo.Element.uncache(this.id);
7468     },
7469
7470     remove : function(){
7471         this.destroy();
7472     },
7473
7474     // private
7475     beginUpdate : function(){
7476         this.updating = true;
7477     },
7478
7479     // private
7480     endUpdate : function(){
7481         this.updating = false;
7482         this.sync(true);
7483     },
7484
7485     // private
7486     hideUnders : function(negOffset){
7487         if(this.shadow){
7488             this.shadow.hide();
7489         }
7490         this.hideShim();
7491     },
7492
7493     // private
7494     constrainXY : function(){
7495         if(this.constrain){
7496             var vw = Roo.lib.Dom.getViewWidth(),
7497                 vh = Roo.lib.Dom.getViewHeight();
7498             var s = Roo.get(document).getScroll();
7499
7500             var xy = this.getXY();
7501             var x = xy[0], y = xy[1];   
7502             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7503             // only move it if it needs it
7504             var moved = false;
7505             // first validate right/bottom
7506             if((x + w) > vw+s.left){
7507                 x = vw - w - this.shadowOffset;
7508                 moved = true;
7509             }
7510             if((y + h) > vh+s.top){
7511                 y = vh - h - this.shadowOffset;
7512                 moved = true;
7513             }
7514             // then make sure top/left isn't negative
7515             if(x < s.left){
7516                 x = s.left;
7517                 moved = true;
7518             }
7519             if(y < s.top){
7520                 y = s.top;
7521                 moved = true;
7522             }
7523             if(moved){
7524                 if(this.avoidY){
7525                     var ay = this.avoidY;
7526                     if(y <= ay && (y+h) >= ay){
7527                         y = ay-h-5;   
7528                     }
7529                 }
7530                 xy = [x, y];
7531                 this.storeXY(xy);
7532                 supr.setXY.call(this, xy);
7533                 this.sync();
7534             }
7535         }
7536     },
7537
7538     isVisible : function(){
7539         return this.visible;    
7540     },
7541
7542     // private
7543     showAction : function(){
7544         this.visible = true; // track visibility to prevent getStyle calls
7545         if(this.useDisplay === true){
7546             this.setDisplayed("");
7547         }else if(this.lastXY){
7548             supr.setXY.call(this, this.lastXY);
7549         }else if(this.lastLT){
7550             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7551         }
7552     },
7553
7554     // private
7555     hideAction : function(){
7556         this.visible = false;
7557         if(this.useDisplay === true){
7558             this.setDisplayed(false);
7559         }else{
7560             this.setLeftTop(-10000,-10000);
7561         }
7562     },
7563
7564     // overridden Element method
7565     setVisible : function(v, a, d, c, e){
7566         if(v){
7567             this.showAction();
7568         }
7569         if(a && v){
7570             var cb = function(){
7571                 this.sync(true);
7572                 if(c){
7573                     c();
7574                 }
7575             }.createDelegate(this);
7576             supr.setVisible.call(this, true, true, d, cb, e);
7577         }else{
7578             if(!v){
7579                 this.hideUnders(true);
7580             }
7581             var cb = c;
7582             if(a){
7583                 cb = function(){
7584                     this.hideAction();
7585                     if(c){
7586                         c();
7587                     }
7588                 }.createDelegate(this);
7589             }
7590             supr.setVisible.call(this, v, a, d, cb, e);
7591             if(v){
7592                 this.sync(true);
7593             }else if(!a){
7594                 this.hideAction();
7595             }
7596         }
7597     },
7598
7599     storeXY : function(xy){
7600         delete this.lastLT;
7601         this.lastXY = xy;
7602     },
7603
7604     storeLeftTop : function(left, top){
7605         delete this.lastXY;
7606         this.lastLT = [left, top];
7607     },
7608
7609     // private
7610     beforeFx : function(){
7611         this.beforeAction();
7612         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7613     },
7614
7615     // private
7616     afterFx : function(){
7617         Roo.Layer.superclass.afterFx.apply(this, arguments);
7618         this.sync(this.isVisible());
7619     },
7620
7621     // private
7622     beforeAction : function(){
7623         if(!this.updating && this.shadow){
7624             this.shadow.hide();
7625         }
7626     },
7627
7628     // overridden Element method
7629     setLeft : function(left){
7630         this.storeLeftTop(left, this.getTop(true));
7631         supr.setLeft.apply(this, arguments);
7632         this.sync();
7633     },
7634
7635     setTop : function(top){
7636         this.storeLeftTop(this.getLeft(true), top);
7637         supr.setTop.apply(this, arguments);
7638         this.sync();
7639     },
7640
7641     setLeftTop : function(left, top){
7642         this.storeLeftTop(left, top);
7643         supr.setLeftTop.apply(this, arguments);
7644         this.sync();
7645     },
7646
7647     setXY : function(xy, a, d, c, e){
7648         this.fixDisplay();
7649         this.beforeAction();
7650         this.storeXY(xy);
7651         var cb = this.createCB(c);
7652         supr.setXY.call(this, xy, a, d, cb, e);
7653         if(!a){
7654             cb();
7655         }
7656     },
7657
7658     // private
7659     createCB : function(c){
7660         var el = this;
7661         return function(){
7662             el.constrainXY();
7663             el.sync(true);
7664             if(c){
7665                 c();
7666             }
7667         };
7668     },
7669
7670     // overridden Element method
7671     setX : function(x, a, d, c, e){
7672         this.setXY([x, this.getY()], a, d, c, e);
7673     },
7674
7675     // overridden Element method
7676     setY : function(y, a, d, c, e){
7677         this.setXY([this.getX(), y], a, d, c, e);
7678     },
7679
7680     // overridden Element method
7681     setSize : function(w, h, a, d, c, e){
7682         this.beforeAction();
7683         var cb = this.createCB(c);
7684         supr.setSize.call(this, w, h, a, d, cb, e);
7685         if(!a){
7686             cb();
7687         }
7688     },
7689
7690     // overridden Element method
7691     setWidth : function(w, a, d, c, e){
7692         this.beforeAction();
7693         var cb = this.createCB(c);
7694         supr.setWidth.call(this, w, a, d, cb, e);
7695         if(!a){
7696             cb();
7697         }
7698     },
7699
7700     // overridden Element method
7701     setHeight : function(h, a, d, c, e){
7702         this.beforeAction();
7703         var cb = this.createCB(c);
7704         supr.setHeight.call(this, h, a, d, cb, e);
7705         if(!a){
7706             cb();
7707         }
7708     },
7709
7710     // overridden Element method
7711     setBounds : function(x, y, w, h, a, d, c, e){
7712         this.beforeAction();
7713         var cb = this.createCB(c);
7714         if(!a){
7715             this.storeXY([x, y]);
7716             supr.setXY.call(this, [x, y]);
7717             supr.setSize.call(this, w, h, a, d, cb, e);
7718             cb();
7719         }else{
7720             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7721         }
7722         return this;
7723     },
7724     
7725     /**
7726      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7727      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7728      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7729      * @param {Number} zindex The new z-index to set
7730      * @return {this} The Layer
7731      */
7732     setZIndex : function(zindex){
7733         this.zindex = zindex;
7734         this.setStyle("z-index", zindex + 2);
7735         if(this.shadow){
7736             this.shadow.setZIndex(zindex + 1);
7737         }
7738         if(this.shim){
7739             this.shim.setStyle("z-index", zindex);
7740         }
7741     }
7742 });
7743 })();/*
7744  * Based on:
7745  * Ext JS Library 1.1.1
7746  * Copyright(c) 2006-2007, Ext JS, LLC.
7747  *
7748  * Originally Released Under LGPL - original licence link has changed is not relivant.
7749  *
7750  * Fork - LGPL
7751  * <script type="text/javascript">
7752  */
7753
7754
7755 /**
7756  * @class Roo.Shadow
7757  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7758  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7759  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7760  * @constructor
7761  * Create a new Shadow
7762  * @param {Object} config The config object
7763  */
7764 Roo.Shadow = function(config){
7765     Roo.apply(this, config);
7766     if(typeof this.mode != "string"){
7767         this.mode = this.defaultMode;
7768     }
7769     var o = this.offset, a = {h: 0};
7770     var rad = Math.floor(this.offset/2);
7771     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7772         case "drop":
7773             a.w = 0;
7774             a.l = a.t = o;
7775             a.t -= 1;
7776             if(Roo.isIE){
7777                 a.l -= this.offset + rad;
7778                 a.t -= this.offset + rad;
7779                 a.w -= rad;
7780                 a.h -= rad;
7781                 a.t += 1;
7782             }
7783         break;
7784         case "sides":
7785             a.w = (o*2);
7786             a.l = -o;
7787             a.t = o-1;
7788             if(Roo.isIE){
7789                 a.l -= (this.offset - rad);
7790                 a.t -= this.offset + rad;
7791                 a.l += 1;
7792                 a.w -= (this.offset - rad)*2;
7793                 a.w -= rad + 1;
7794                 a.h -= 1;
7795             }
7796         break;
7797         case "frame":
7798             a.w = a.h = (o*2);
7799             a.l = a.t = -o;
7800             a.t += 1;
7801             a.h -= 2;
7802             if(Roo.isIE){
7803                 a.l -= (this.offset - rad);
7804                 a.t -= (this.offset - rad);
7805                 a.l += 1;
7806                 a.w -= (this.offset + rad + 1);
7807                 a.h -= (this.offset + rad);
7808                 a.h += 1;
7809             }
7810         break;
7811     };
7812
7813     this.adjusts = a;
7814 };
7815
7816 Roo.Shadow.prototype = {
7817     /**
7818      * @cfg {String} mode
7819      * The shadow display mode.  Supports the following options:<br />
7820      * sides: Shadow displays on both sides and bottom only<br />
7821      * frame: Shadow displays equally on all four sides<br />
7822      * drop: Traditional bottom-right drop shadow (default)
7823      */
7824     /**
7825      * @cfg {String} offset
7826      * The number of pixels to offset the shadow from the element (defaults to 4)
7827      */
7828     offset: 4,
7829
7830     // private
7831     defaultMode: "drop",
7832
7833     /**
7834      * Displays the shadow under the target element
7835      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7836      */
7837     show : function(target){
7838         target = Roo.get(target);
7839         if(!this.el){
7840             this.el = Roo.Shadow.Pool.pull();
7841             if(this.el.dom.nextSibling != target.dom){
7842                 this.el.insertBefore(target);
7843             }
7844         }
7845         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7846         if(Roo.isIE){
7847             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7848         }
7849         this.realign(
7850             target.getLeft(true),
7851             target.getTop(true),
7852             target.getWidth(),
7853             target.getHeight()
7854         );
7855         this.el.dom.style.display = "block";
7856     },
7857
7858     /**
7859      * Returns true if the shadow is visible, else false
7860      */
7861     isVisible : function(){
7862         return this.el ? true : false;  
7863     },
7864
7865     /**
7866      * Direct alignment when values are already available. Show must be called at least once before
7867      * calling this method to ensure it is initialized.
7868      * @param {Number} left The target element left position
7869      * @param {Number} top The target element top position
7870      * @param {Number} width The target element width
7871      * @param {Number} height The target element height
7872      */
7873     realign : function(l, t, w, h){
7874         if(!this.el){
7875             return;
7876         }
7877         var a = this.adjusts, d = this.el.dom, s = d.style;
7878         var iea = 0;
7879         s.left = (l+a.l)+"px";
7880         s.top = (t+a.t)+"px";
7881         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7882  
7883         if(s.width != sws || s.height != shs){
7884             s.width = sws;
7885             s.height = shs;
7886             if(!Roo.isIE){
7887                 var cn = d.childNodes;
7888                 var sww = Math.max(0, (sw-12))+"px";
7889                 cn[0].childNodes[1].style.width = sww;
7890                 cn[1].childNodes[1].style.width = sww;
7891                 cn[2].childNodes[1].style.width = sww;
7892                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7893             }
7894         }
7895     },
7896
7897     /**
7898      * Hides this shadow
7899      */
7900     hide : function(){
7901         if(this.el){
7902             this.el.dom.style.display = "none";
7903             Roo.Shadow.Pool.push(this.el);
7904             delete this.el;
7905         }
7906     },
7907
7908     /**
7909      * Adjust the z-index of this shadow
7910      * @param {Number} zindex The new z-index
7911      */
7912     setZIndex : function(z){
7913         this.zIndex = z;
7914         if(this.el){
7915             this.el.setStyle("z-index", z);
7916         }
7917     }
7918 };
7919
7920 // Private utility class that manages the internal Shadow cache
7921 Roo.Shadow.Pool = function(){
7922     var p = [];
7923     var markup = Roo.isIE ?
7924                  '<div class="x-ie-shadow"></div>' :
7925                  '<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>';
7926     return {
7927         pull : function(){
7928             var sh = p.shift();
7929             if(!sh){
7930                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7931                 sh.autoBoxAdjust = false;
7932             }
7933             return sh;
7934         },
7935
7936         push : function(sh){
7937             p.push(sh);
7938         }
7939     };
7940 }();/*
7941  * Based on:
7942  * Ext JS Library 1.1.1
7943  * Copyright(c) 2006-2007, Ext JS, LLC.
7944  *
7945  * Originally Released Under LGPL - original licence link has changed is not relivant.
7946  *
7947  * Fork - LGPL
7948  * <script type="text/javascript">
7949  */
7950
7951
7952 /**
7953  * @class Roo.SplitBar
7954  * @extends Roo.util.Observable
7955  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7956  * <br><br>
7957  * Usage:
7958  * <pre><code>
7959 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7960                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7961 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7962 split.minSize = 100;
7963 split.maxSize = 600;
7964 split.animate = true;
7965 split.on('moved', splitterMoved);
7966 </code></pre>
7967  * @constructor
7968  * Create a new SplitBar
7969  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7970  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7971  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7972  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7973                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7974                         position of the SplitBar).
7975  */
7976 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7977     
7978     /** @private */
7979     this.el = Roo.get(dragElement, true);
7980     this.el.dom.unselectable = "on";
7981     /** @private */
7982     this.resizingEl = Roo.get(resizingElement, true);
7983
7984     /**
7985      * @private
7986      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7987      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7988      * @type Number
7989      */
7990     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7991     
7992     /**
7993      * The minimum size of the resizing element. (Defaults to 0)
7994      * @type Number
7995      */
7996     this.minSize = 0;
7997     
7998     /**
7999      * The maximum size of the resizing element. (Defaults to 2000)
8000      * @type Number
8001      */
8002     this.maxSize = 2000;
8003     
8004     /**
8005      * Whether to animate the transition to the new size
8006      * @type Boolean
8007      */
8008     this.animate = false;
8009     
8010     /**
8011      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8012      * @type Boolean
8013      */
8014     this.useShim = false;
8015     
8016     /** @private */
8017     this.shim = null;
8018     
8019     if(!existingProxy){
8020         /** @private */
8021         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8022     }else{
8023         this.proxy = Roo.get(existingProxy).dom;
8024     }
8025     /** @private */
8026     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8027     
8028     /** @private */
8029     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8030     
8031     /** @private */
8032     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8033     
8034     /** @private */
8035     this.dragSpecs = {};
8036     
8037     /**
8038      * @private The adapter to use to positon and resize elements
8039      */
8040     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8041     this.adapter.init(this);
8042     
8043     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8044         /** @private */
8045         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8046         this.el.addClass("x-splitbar-h");
8047     }else{
8048         /** @private */
8049         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8050         this.el.addClass("x-splitbar-v");
8051     }
8052     
8053     this.addEvents({
8054         /**
8055          * @event resize
8056          * Fires when the splitter is moved (alias for {@link #event-moved})
8057          * @param {Roo.SplitBar} this
8058          * @param {Number} newSize the new width or height
8059          */
8060         "resize" : true,
8061         /**
8062          * @event moved
8063          * Fires when the splitter is moved
8064          * @param {Roo.SplitBar} this
8065          * @param {Number} newSize the new width or height
8066          */
8067         "moved" : true,
8068         /**
8069          * @event beforeresize
8070          * Fires before the splitter is dragged
8071          * @param {Roo.SplitBar} this
8072          */
8073         "beforeresize" : true,
8074
8075         "beforeapply" : true
8076     });
8077
8078     Roo.util.Observable.call(this);
8079 };
8080
8081 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8082     onStartProxyDrag : function(x, y){
8083         this.fireEvent("beforeresize", this);
8084         if(!this.overlay){
8085             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8086             o.unselectable();
8087             o.enableDisplayMode("block");
8088             // all splitbars share the same overlay
8089             Roo.SplitBar.prototype.overlay = o;
8090         }
8091         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8092         this.overlay.show();
8093         Roo.get(this.proxy).setDisplayed("block");
8094         var size = this.adapter.getElementSize(this);
8095         this.activeMinSize = this.getMinimumSize();;
8096         this.activeMaxSize = this.getMaximumSize();;
8097         var c1 = size - this.activeMinSize;
8098         var c2 = Math.max(this.activeMaxSize - size, 0);
8099         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8100             this.dd.resetConstraints();
8101             this.dd.setXConstraint(
8102                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8103                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8104             );
8105             this.dd.setYConstraint(0, 0);
8106         }else{
8107             this.dd.resetConstraints();
8108             this.dd.setXConstraint(0, 0);
8109             this.dd.setYConstraint(
8110                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8111                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8112             );
8113          }
8114         this.dragSpecs.startSize = size;
8115         this.dragSpecs.startPoint = [x, y];
8116         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8117     },
8118     
8119     /** 
8120      * @private Called after the drag operation by the DDProxy
8121      */
8122     onEndProxyDrag : function(e){
8123         Roo.get(this.proxy).setDisplayed(false);
8124         var endPoint = Roo.lib.Event.getXY(e);
8125         if(this.overlay){
8126             this.overlay.hide();
8127         }
8128         var newSize;
8129         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8130             newSize = this.dragSpecs.startSize + 
8131                 (this.placement == Roo.SplitBar.LEFT ?
8132                     endPoint[0] - this.dragSpecs.startPoint[0] :
8133                     this.dragSpecs.startPoint[0] - endPoint[0]
8134                 );
8135         }else{
8136             newSize = this.dragSpecs.startSize + 
8137                 (this.placement == Roo.SplitBar.TOP ?
8138                     endPoint[1] - this.dragSpecs.startPoint[1] :
8139                     this.dragSpecs.startPoint[1] - endPoint[1]
8140                 );
8141         }
8142         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8143         if(newSize != this.dragSpecs.startSize){
8144             if(this.fireEvent('beforeapply', this, newSize) !== false){
8145                 this.adapter.setElementSize(this, newSize);
8146                 this.fireEvent("moved", this, newSize);
8147                 this.fireEvent("resize", this, newSize);
8148             }
8149         }
8150     },
8151     
8152     /**
8153      * Get the adapter this SplitBar uses
8154      * @return The adapter object
8155      */
8156     getAdapter : function(){
8157         return this.adapter;
8158     },
8159     
8160     /**
8161      * Set the adapter this SplitBar uses
8162      * @param {Object} adapter A SplitBar adapter object
8163      */
8164     setAdapter : function(adapter){
8165         this.adapter = adapter;
8166         this.adapter.init(this);
8167     },
8168     
8169     /**
8170      * Gets the minimum size for the resizing element
8171      * @return {Number} The minimum size
8172      */
8173     getMinimumSize : function(){
8174         return this.minSize;
8175     },
8176     
8177     /**
8178      * Sets the minimum size for the resizing element
8179      * @param {Number} minSize The minimum size
8180      */
8181     setMinimumSize : function(minSize){
8182         this.minSize = minSize;
8183     },
8184     
8185     /**
8186      * Gets the maximum size for the resizing element
8187      * @return {Number} The maximum size
8188      */
8189     getMaximumSize : function(){
8190         return this.maxSize;
8191     },
8192     
8193     /**
8194      * Sets the maximum size for the resizing element
8195      * @param {Number} maxSize The maximum size
8196      */
8197     setMaximumSize : function(maxSize){
8198         this.maxSize = maxSize;
8199     },
8200     
8201     /**
8202      * Sets the initialize size for the resizing element
8203      * @param {Number} size The initial size
8204      */
8205     setCurrentSize : function(size){
8206         var oldAnimate = this.animate;
8207         this.animate = false;
8208         this.adapter.setElementSize(this, size);
8209         this.animate = oldAnimate;
8210     },
8211     
8212     /**
8213      * Destroy this splitbar. 
8214      * @param {Boolean} removeEl True to remove the element
8215      */
8216     destroy : function(removeEl){
8217         if(this.shim){
8218             this.shim.remove();
8219         }
8220         this.dd.unreg();
8221         this.proxy.parentNode.removeChild(this.proxy);
8222         if(removeEl){
8223             this.el.remove();
8224         }
8225     }
8226 });
8227
8228 /**
8229  * @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.
8230  */
8231 Roo.SplitBar.createProxy = function(dir){
8232     var proxy = new Roo.Element(document.createElement("div"));
8233     proxy.unselectable();
8234     var cls = 'x-splitbar-proxy';
8235     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8236     document.body.appendChild(proxy.dom);
8237     return proxy.dom;
8238 };
8239
8240 /** 
8241  * @class Roo.SplitBar.BasicLayoutAdapter
8242  * Default Adapter. It assumes the splitter and resizing element are not positioned
8243  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8244  */
8245 Roo.SplitBar.BasicLayoutAdapter = function(){
8246 };
8247
8248 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8249     // do nothing for now
8250     init : function(s){
8251     
8252     },
8253     /**
8254      * Called before drag operations to get the current size of the resizing element. 
8255      * @param {Roo.SplitBar} s The SplitBar using this adapter
8256      */
8257      getElementSize : function(s){
8258         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8259             return s.resizingEl.getWidth();
8260         }else{
8261             return s.resizingEl.getHeight();
8262         }
8263     },
8264     
8265     /**
8266      * Called after drag operations to set the size of the resizing element.
8267      * @param {Roo.SplitBar} s The SplitBar using this adapter
8268      * @param {Number} newSize The new size to set
8269      * @param {Function} onComplete A function to be invoked when resizing is complete
8270      */
8271     setElementSize : function(s, newSize, onComplete){
8272         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8273             if(!s.animate){
8274                 s.resizingEl.setWidth(newSize);
8275                 if(onComplete){
8276                     onComplete(s, newSize);
8277                 }
8278             }else{
8279                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8280             }
8281         }else{
8282             
8283             if(!s.animate){
8284                 s.resizingEl.setHeight(newSize);
8285                 if(onComplete){
8286                     onComplete(s, newSize);
8287                 }
8288             }else{
8289                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8290             }
8291         }
8292     }
8293 };
8294
8295 /** 
8296  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8297  * @extends Roo.SplitBar.BasicLayoutAdapter
8298  * Adapter that  moves the splitter element to align with the resized sizing element. 
8299  * Used with an absolute positioned SplitBar.
8300  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8301  * document.body, make sure you assign an id to the body element.
8302  */
8303 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8304     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8305     this.container = Roo.get(container);
8306 };
8307
8308 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8309     init : function(s){
8310         this.basic.init(s);
8311     },
8312     
8313     getElementSize : function(s){
8314         return this.basic.getElementSize(s);
8315     },
8316     
8317     setElementSize : function(s, newSize, onComplete){
8318         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8319     },
8320     
8321     moveSplitter : function(s){
8322         var yes = Roo.SplitBar;
8323         switch(s.placement){
8324             case yes.LEFT:
8325                 s.el.setX(s.resizingEl.getRight());
8326                 break;
8327             case yes.RIGHT:
8328                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8329                 break;
8330             case yes.TOP:
8331                 s.el.setY(s.resizingEl.getBottom());
8332                 break;
8333             case yes.BOTTOM:
8334                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8335                 break;
8336         }
8337     }
8338 };
8339
8340 /**
8341  * Orientation constant - Create a vertical SplitBar
8342  * @static
8343  * @type Number
8344  */
8345 Roo.SplitBar.VERTICAL = 1;
8346
8347 /**
8348  * Orientation constant - Create a horizontal SplitBar
8349  * @static
8350  * @type Number
8351  */
8352 Roo.SplitBar.HORIZONTAL = 2;
8353
8354 /**
8355  * Placement constant - The resizing element is to the left of the splitter element
8356  * @static
8357  * @type Number
8358  */
8359 Roo.SplitBar.LEFT = 1;
8360
8361 /**
8362  * Placement constant - The resizing element is to the right of the splitter element
8363  * @static
8364  * @type Number
8365  */
8366 Roo.SplitBar.RIGHT = 2;
8367
8368 /**
8369  * Placement constant - The resizing element is positioned above the splitter element
8370  * @static
8371  * @type Number
8372  */
8373 Roo.SplitBar.TOP = 3;
8374
8375 /**
8376  * Placement constant - The resizing element is positioned under splitter element
8377  * @static
8378  * @type Number
8379  */
8380 Roo.SplitBar.BOTTOM = 4;
8381 /*
8382  * Based on:
8383  * Ext JS Library 1.1.1
8384  * Copyright(c) 2006-2007, Ext JS, LLC.
8385  *
8386  * Originally Released Under LGPL - original licence link has changed is not relivant.
8387  *
8388  * Fork - LGPL
8389  * <script type="text/javascript">
8390  */
8391
8392 /**
8393  * @class Roo.View
8394  * @extends Roo.util.Observable
8395  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8396  * This class also supports single and multi selection modes. <br>
8397  * Create a data model bound view:
8398  <pre><code>
8399  var store = new Roo.data.Store(...);
8400
8401  var view = new Roo.View({
8402     el : "my-element",
8403     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8404  
8405     singleSelect: true,
8406     selectedClass: "ydataview-selected",
8407     store: store
8408  });
8409
8410  // listen for node click?
8411  view.on("click", function(vw, index, node, e){
8412  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8413  });
8414
8415  // load XML data
8416  dataModel.load("foobar.xml");
8417  </code></pre>
8418  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8419  * <br><br>
8420  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8421  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8422  * 
8423  * Note: old style constructor is still suported (container, template, config)
8424  * 
8425  * @constructor
8426  * Create a new View
8427  * @param {Object} config The config object
8428  * 
8429  */
8430 Roo.View = function(config, depreciated_tpl, depreciated_config){
8431     
8432     this.parent = false;
8433     
8434     if (typeof(depreciated_tpl) == 'undefined') {
8435         // new way.. - universal constructor.
8436         Roo.apply(this, config);
8437         this.el  = Roo.get(this.el);
8438     } else {
8439         // old format..
8440         this.el  = Roo.get(config);
8441         this.tpl = depreciated_tpl;
8442         Roo.apply(this, depreciated_config);
8443     }
8444     this.wrapEl  = this.el.wrap().wrap();
8445     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8446     
8447     
8448     if(typeof(this.tpl) == "string"){
8449         this.tpl = new Roo.Template(this.tpl);
8450     } else {
8451         // support xtype ctors..
8452         this.tpl = new Roo.factory(this.tpl, Roo);
8453     }
8454     
8455     
8456     this.tpl.compile();
8457     
8458     /** @private */
8459     this.addEvents({
8460         /**
8461          * @event beforeclick
8462          * Fires before a click is processed. Returns false to cancel the default action.
8463          * @param {Roo.View} this
8464          * @param {Number} index The index of the target node
8465          * @param {HTMLElement} node The target node
8466          * @param {Roo.EventObject} e The raw event object
8467          */
8468             "beforeclick" : true,
8469         /**
8470          * @event click
8471          * Fires when a template node is clicked.
8472          * @param {Roo.View} this
8473          * @param {Number} index The index of the target node
8474          * @param {HTMLElement} node The target node
8475          * @param {Roo.EventObject} e The raw event object
8476          */
8477             "click" : true,
8478         /**
8479          * @event dblclick
8480          * Fires when a template node is double clicked.
8481          * @param {Roo.View} this
8482          * @param {Number} index The index of the target node
8483          * @param {HTMLElement} node The target node
8484          * @param {Roo.EventObject} e The raw event object
8485          */
8486             "dblclick" : true,
8487         /**
8488          * @event contextmenu
8489          * Fires when a template node is right clicked.
8490          * @param {Roo.View} this
8491          * @param {Number} index The index of the target node
8492          * @param {HTMLElement} node The target node
8493          * @param {Roo.EventObject} e The raw event object
8494          */
8495             "contextmenu" : true,
8496         /**
8497          * @event selectionchange
8498          * Fires when the selected nodes change.
8499          * @param {Roo.View} this
8500          * @param {Array} selections Array of the selected nodes
8501          */
8502             "selectionchange" : true,
8503     
8504         /**
8505          * @event beforeselect
8506          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8507          * @param {Roo.View} this
8508          * @param {HTMLElement} node The node to be selected
8509          * @param {Array} selections Array of currently selected nodes
8510          */
8511             "beforeselect" : true,
8512         /**
8513          * @event preparedata
8514          * Fires on every row to render, to allow you to change the data.
8515          * @param {Roo.View} this
8516          * @param {Object} data to be rendered (change this)
8517          */
8518           "preparedata" : true
8519           
8520           
8521         });
8522
8523
8524
8525     this.el.on({
8526         "click": this.onClick,
8527         "dblclick": this.onDblClick,
8528         "contextmenu": this.onContextMenu,
8529         scope:this
8530     });
8531
8532     this.selections = [];
8533     this.nodes = [];
8534     this.cmp = new Roo.CompositeElementLite([]);
8535     if(this.store){
8536         this.store = Roo.factory(this.store, Roo.data);
8537         this.setStore(this.store, true);
8538     }
8539     
8540     if ( this.footer && this.footer.xtype) {
8541            
8542          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8543         
8544         this.footer.dataSource = this.store
8545         this.footer.container = fctr;
8546         this.footer = Roo.factory(this.footer, Roo);
8547         fctr.insertFirst(this.el);
8548         
8549         // this is a bit insane - as the paging toolbar seems to detach the el..
8550 //        dom.parentNode.parentNode.parentNode
8551          // they get detached?
8552     }
8553     
8554     
8555     Roo.View.superclass.constructor.call(this);
8556     
8557     
8558 };
8559
8560 Roo.extend(Roo.View, Roo.util.Observable, {
8561     
8562      /**
8563      * @cfg {Roo.data.Store} store Data store to load data from.
8564      */
8565     store : false,
8566     
8567     /**
8568      * @cfg {String|Roo.Element} el The container element.
8569      */
8570     el : '',
8571     
8572     /**
8573      * @cfg {String|Roo.Template} tpl The template used by this View 
8574      */
8575     tpl : false,
8576     /**
8577      * @cfg {String} dataName the named area of the template to use as the data area
8578      *                          Works with domtemplates roo-name="name"
8579      */
8580     dataName: false,
8581     /**
8582      * @cfg {String} selectedClass The css class to add to selected nodes
8583      */
8584     selectedClass : "x-view-selected",
8585      /**
8586      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8587      */
8588     emptyText : "",
8589     
8590     /**
8591      * @cfg {String} text to display on mask (default Loading)
8592      */
8593     mask : false,
8594     /**
8595      * @cfg {Boolean} multiSelect Allow multiple selection
8596      */
8597     multiSelect : false,
8598     /**
8599      * @cfg {Boolean} singleSelect Allow single selection
8600      */
8601     singleSelect:  false,
8602     
8603     /**
8604      * @cfg {Boolean} toggleSelect - selecting 
8605      */
8606     toggleSelect : false,
8607     
8608     /**
8609      * @cfg {Boolean} tickable - selecting 
8610      */
8611     tickable : false,
8612     
8613     /**
8614      * Returns the element this view is bound to.
8615      * @return {Roo.Element}
8616      */
8617     getEl : function(){
8618         return this.wrapEl;
8619     },
8620     
8621     
8622
8623     /**
8624      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8625      */
8626     refresh : function(){
8627         //Roo.log('refresh');
8628         var t = this.tpl;
8629         
8630         // if we are using something like 'domtemplate', then
8631         // the what gets used is:
8632         // t.applySubtemplate(NAME, data, wrapping data..)
8633         // the outer template then get' applied with
8634         //     the store 'extra data'
8635         // and the body get's added to the
8636         //      roo-name="data" node?
8637         //      <span class='roo-tpl-{name}'></span> ?????
8638         
8639         
8640         
8641         this.clearSelections();
8642         this.el.update("");
8643         var html = [];
8644         var records = this.store.getRange();
8645         if(records.length < 1) {
8646             
8647             // is this valid??  = should it render a template??
8648             
8649             this.el.update(this.emptyText);
8650             return;
8651         }
8652         var el = this.el;
8653         if (this.dataName) {
8654             this.el.update(t.apply(this.store.meta)); //????
8655             el = this.el.child('.roo-tpl-' + this.dataName);
8656         }
8657         
8658         for(var i = 0, len = records.length; i < len; i++){
8659             var data = this.prepareData(records[i].data, i, records[i]);
8660             this.fireEvent("preparedata", this, data, i, records[i]);
8661             
8662             var d = Roo.apply({}, data);
8663             
8664             if(this.tickable){
8665                 Roo.apply(d, {'roo-id' : Roo.id()});
8666                 
8667                 var _this = this;
8668             
8669                 Roo.each(this.parent.item, function(item){
8670                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8671                         return;
8672                     }
8673                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8674                 });
8675             }
8676             
8677             html[html.length] = Roo.util.Format.trim(
8678                 this.dataName ?
8679                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8680                     t.apply(d)
8681             );
8682         }
8683         
8684         
8685         
8686         el.update(html.join(""));
8687         this.nodes = el.dom.childNodes;
8688         this.updateIndexes(0);
8689     },
8690     
8691
8692     /**
8693      * Function to override to reformat the data that is sent to
8694      * the template for each node.
8695      * DEPRICATED - use the preparedata event handler.
8696      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8697      * a JSON object for an UpdateManager bound view).
8698      */
8699     prepareData : function(data, index, record)
8700     {
8701         this.fireEvent("preparedata", this, data, index, record);
8702         return data;
8703     },
8704
8705     onUpdate : function(ds, record){
8706         // Roo.log('on update');   
8707         this.clearSelections();
8708         var index = this.store.indexOf(record);
8709         var n = this.nodes[index];
8710         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8711         n.parentNode.removeChild(n);
8712         this.updateIndexes(index, index);
8713     },
8714
8715     
8716     
8717 // --------- FIXME     
8718     onAdd : function(ds, records, index)
8719     {
8720         //Roo.log(['on Add', ds, records, index] );        
8721         this.clearSelections();
8722         if(this.nodes.length == 0){
8723             this.refresh();
8724             return;
8725         }
8726         var n = this.nodes[index];
8727         for(var i = 0, len = records.length; i < len; i++){
8728             var d = this.prepareData(records[i].data, i, records[i]);
8729             if(n){
8730                 this.tpl.insertBefore(n, d);
8731             }else{
8732                 
8733                 this.tpl.append(this.el, d);
8734             }
8735         }
8736         this.updateIndexes(index);
8737     },
8738
8739     onRemove : function(ds, record, index){
8740        // Roo.log('onRemove');
8741         this.clearSelections();
8742         var el = this.dataName  ?
8743             this.el.child('.roo-tpl-' + this.dataName) :
8744             this.el; 
8745         
8746         el.dom.removeChild(this.nodes[index]);
8747         this.updateIndexes(index);
8748     },
8749
8750     /**
8751      * Refresh an individual node.
8752      * @param {Number} index
8753      */
8754     refreshNode : function(index){
8755         this.onUpdate(this.store, this.store.getAt(index));
8756     },
8757
8758     updateIndexes : function(startIndex, endIndex){
8759         var ns = this.nodes;
8760         startIndex = startIndex || 0;
8761         endIndex = endIndex || ns.length - 1;
8762         for(var i = startIndex; i <= endIndex; i++){
8763             ns[i].nodeIndex = i;
8764         }
8765     },
8766
8767     /**
8768      * Changes the data store this view uses and refresh the view.
8769      * @param {Store} store
8770      */
8771     setStore : function(store, initial){
8772         if(!initial && this.store){
8773             this.store.un("datachanged", this.refresh);
8774             this.store.un("add", this.onAdd);
8775             this.store.un("remove", this.onRemove);
8776             this.store.un("update", this.onUpdate);
8777             this.store.un("clear", this.refresh);
8778             this.store.un("beforeload", this.onBeforeLoad);
8779             this.store.un("load", this.onLoad);
8780             this.store.un("loadexception", this.onLoad);
8781         }
8782         if(store){
8783           
8784             store.on("datachanged", this.refresh, this);
8785             store.on("add", this.onAdd, this);
8786             store.on("remove", this.onRemove, this);
8787             store.on("update", this.onUpdate, this);
8788             store.on("clear", this.refresh, this);
8789             store.on("beforeload", this.onBeforeLoad, this);
8790             store.on("load", this.onLoad, this);
8791             store.on("loadexception", this.onLoad, this);
8792         }
8793         
8794         if(store){
8795             this.refresh();
8796         }
8797     },
8798     /**
8799      * onbeforeLoad - masks the loading area.
8800      *
8801      */
8802     onBeforeLoad : function(store,opts)
8803     {
8804          //Roo.log('onBeforeLoad');   
8805         if (!opts.add) {
8806             this.el.update("");
8807         }
8808         this.el.mask(this.mask ? this.mask : "Loading" ); 
8809     },
8810     onLoad : function ()
8811     {
8812         this.el.unmask();
8813     },
8814     
8815
8816     /**
8817      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8818      * @param {HTMLElement} node
8819      * @return {HTMLElement} The template node
8820      */
8821     findItemFromChild : function(node){
8822         var el = this.dataName  ?
8823             this.el.child('.roo-tpl-' + this.dataName,true) :
8824             this.el.dom; 
8825         
8826         if(!node || node.parentNode == el){
8827                     return node;
8828             }
8829             var p = node.parentNode;
8830             while(p && p != el){
8831             if(p.parentNode == el){
8832                 return p;
8833             }
8834             p = p.parentNode;
8835         }
8836             return null;
8837     },
8838
8839     /** @ignore */
8840     onClick : function(e){
8841         var item = this.findItemFromChild(e.getTarget());
8842         if(item){
8843             var index = this.indexOf(item);
8844             if(this.onItemClick(item, index, e) !== false){
8845                 this.fireEvent("click", this, index, item, e);
8846             }
8847         }else{
8848             this.clearSelections();
8849         }
8850     },
8851
8852     /** @ignore */
8853     onContextMenu : function(e){
8854         var item = this.findItemFromChild(e.getTarget());
8855         if(item){
8856             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8857         }
8858     },
8859
8860     /** @ignore */
8861     onDblClick : function(e){
8862         var item = this.findItemFromChild(e.getTarget());
8863         if(item){
8864             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8865         }
8866     },
8867
8868     onItemClick : function(item, index, e)
8869     {
8870         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8871             return false;
8872         }
8873         if (this.toggleSelect) {
8874             var m = this.isSelected(item) ? 'unselect' : 'select';
8875             //Roo.log(m);
8876             var _t = this;
8877             _t[m](item, true, false);
8878             return true;
8879         }
8880         if(this.multiSelect || this.singleSelect){
8881             if(this.multiSelect && e.shiftKey && this.lastSelection){
8882                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8883             }else{
8884                 this.select(item, this.multiSelect && e.ctrlKey);
8885                 this.lastSelection = item;
8886             }
8887             
8888             if(!this.tickable){
8889                 e.preventDefault();
8890             }
8891             
8892         }
8893         return true;
8894     },
8895
8896     /**
8897      * Get the number of selected nodes.
8898      * @return {Number}
8899      */
8900     getSelectionCount : function(){
8901         return this.selections.length;
8902     },
8903
8904     /**
8905      * Get the currently selected nodes.
8906      * @return {Array} An array of HTMLElements
8907      */
8908     getSelectedNodes : function(){
8909         return this.selections;
8910     },
8911
8912     /**
8913      * Get the indexes of the selected nodes.
8914      * @return {Array}
8915      */
8916     getSelectedIndexes : function(){
8917         var indexes = [], s = this.selections;
8918         for(var i = 0, len = s.length; i < len; i++){
8919             indexes.push(s[i].nodeIndex);
8920         }
8921         return indexes;
8922     },
8923
8924     /**
8925      * Clear all selections
8926      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8927      */
8928     clearSelections : function(suppressEvent){
8929         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8930             this.cmp.elements = this.selections;
8931             this.cmp.removeClass(this.selectedClass);
8932             this.selections = [];
8933             if(!suppressEvent){
8934                 this.fireEvent("selectionchange", this, this.selections);
8935             }
8936         }
8937     },
8938
8939     /**
8940      * Returns true if the passed node is selected
8941      * @param {HTMLElement/Number} node The node or node index
8942      * @return {Boolean}
8943      */
8944     isSelected : function(node){
8945         var s = this.selections;
8946         if(s.length < 1){
8947             return false;
8948         }
8949         node = this.getNode(node);
8950         return s.indexOf(node) !== -1;
8951     },
8952
8953     /**
8954      * Selects nodes.
8955      * @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
8956      * @param {Boolean} keepExisting (optional) true to keep existing selections
8957      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8958      */
8959     select : function(nodeInfo, keepExisting, suppressEvent){
8960         if(nodeInfo instanceof Array){
8961             if(!keepExisting){
8962                 this.clearSelections(true);
8963             }
8964             for(var i = 0, len = nodeInfo.length; i < len; i++){
8965                 this.select(nodeInfo[i], true, true);
8966             }
8967             return;
8968         } 
8969         var node = this.getNode(nodeInfo);
8970         if(!node || this.isSelected(node)){
8971             return; // already selected.
8972         }
8973         if(!keepExisting){
8974             this.clearSelections(true);
8975         }
8976         
8977         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8978             Roo.fly(node).addClass(this.selectedClass);
8979             this.selections.push(node);
8980             if(!suppressEvent){
8981                 this.fireEvent("selectionchange", this, this.selections);
8982             }
8983         }
8984         
8985         
8986     },
8987       /**
8988      * Unselects nodes.
8989      * @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
8990      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8991      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8992      */
8993     unselect : function(nodeInfo, keepExisting, suppressEvent)
8994     {
8995         if(nodeInfo instanceof Array){
8996             Roo.each(this.selections, function(s) {
8997                 this.unselect(s, nodeInfo);
8998             }, this);
8999             return;
9000         }
9001         var node = this.getNode(nodeInfo);
9002         if(!node || !this.isSelected(node)){
9003             //Roo.log("not selected");
9004             return; // not selected.
9005         }
9006         // fireevent???
9007         var ns = [];
9008         Roo.each(this.selections, function(s) {
9009             if (s == node ) {
9010                 Roo.fly(node).removeClass(this.selectedClass);
9011
9012                 return;
9013             }
9014             ns.push(s);
9015         },this);
9016         
9017         this.selections= ns;
9018         this.fireEvent("selectionchange", this, this.selections);
9019     },
9020
9021     /**
9022      * Gets a template node.
9023      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9024      * @return {HTMLElement} The node or null if it wasn't found
9025      */
9026     getNode : function(nodeInfo){
9027         if(typeof nodeInfo == "string"){
9028             return document.getElementById(nodeInfo);
9029         }else if(typeof nodeInfo == "number"){
9030             return this.nodes[nodeInfo];
9031         }
9032         return nodeInfo;
9033     },
9034
9035     /**
9036      * Gets a range template nodes.
9037      * @param {Number} startIndex
9038      * @param {Number} endIndex
9039      * @return {Array} An array of nodes
9040      */
9041     getNodes : function(start, end){
9042         var ns = this.nodes;
9043         start = start || 0;
9044         end = typeof end == "undefined" ? ns.length - 1 : end;
9045         var nodes = [];
9046         if(start <= end){
9047             for(var i = start; i <= end; i++){
9048                 nodes.push(ns[i]);
9049             }
9050         } else{
9051             for(var i = start; i >= end; i--){
9052                 nodes.push(ns[i]);
9053             }
9054         }
9055         return nodes;
9056     },
9057
9058     /**
9059      * Finds the index of the passed node
9060      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9061      * @return {Number} The index of the node or -1
9062      */
9063     indexOf : function(node){
9064         node = this.getNode(node);
9065         if(typeof node.nodeIndex == "number"){
9066             return node.nodeIndex;
9067         }
9068         var ns = this.nodes;
9069         for(var i = 0, len = ns.length; i < len; i++){
9070             if(ns[i] == node){
9071                 return i;
9072             }
9073         }
9074         return -1;
9075     }
9076 });
9077 /*
9078  * Based on:
9079  * Ext JS Library 1.1.1
9080  * Copyright(c) 2006-2007, Ext JS, LLC.
9081  *
9082  * Originally Released Under LGPL - original licence link has changed is not relivant.
9083  *
9084  * Fork - LGPL
9085  * <script type="text/javascript">
9086  */
9087
9088 /**
9089  * @class Roo.JsonView
9090  * @extends Roo.View
9091  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9092 <pre><code>
9093 var view = new Roo.JsonView({
9094     container: "my-element",
9095     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9096     multiSelect: true, 
9097     jsonRoot: "data" 
9098 });
9099
9100 // listen for node click?
9101 view.on("click", function(vw, index, node, e){
9102     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9103 });
9104
9105 // direct load of JSON data
9106 view.load("foobar.php");
9107
9108 // Example from my blog list
9109 var tpl = new Roo.Template(
9110     '&lt;div class="entry"&gt;' +
9111     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9112     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9113     "&lt;/div&gt;&lt;hr /&gt;"
9114 );
9115
9116 var moreView = new Roo.JsonView({
9117     container :  "entry-list", 
9118     template : tpl,
9119     jsonRoot: "posts"
9120 });
9121 moreView.on("beforerender", this.sortEntries, this);
9122 moreView.load({
9123     url: "/blog/get-posts.php",
9124     params: "allposts=true",
9125     text: "Loading Blog Entries..."
9126 });
9127 </code></pre>
9128
9129 * Note: old code is supported with arguments : (container, template, config)
9130
9131
9132  * @constructor
9133  * Create a new JsonView
9134  * 
9135  * @param {Object} config The config object
9136  * 
9137  */
9138 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9139     
9140     
9141     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9142
9143     var um = this.el.getUpdateManager();
9144     um.setRenderer(this);
9145     um.on("update", this.onLoad, this);
9146     um.on("failure", this.onLoadException, this);
9147
9148     /**
9149      * @event beforerender
9150      * Fires before rendering of the downloaded JSON data.
9151      * @param {Roo.JsonView} this
9152      * @param {Object} data The JSON data loaded
9153      */
9154     /**
9155      * @event load
9156      * Fires when data is loaded.
9157      * @param {Roo.JsonView} this
9158      * @param {Object} data The JSON data loaded
9159      * @param {Object} response The raw Connect response object
9160      */
9161     /**
9162      * @event loadexception
9163      * Fires when loading fails.
9164      * @param {Roo.JsonView} this
9165      * @param {Object} response The raw Connect response object
9166      */
9167     this.addEvents({
9168         'beforerender' : true,
9169         'load' : true,
9170         'loadexception' : true
9171     });
9172 };
9173 Roo.extend(Roo.JsonView, Roo.View, {
9174     /**
9175      * @type {String} The root property in the loaded JSON object that contains the data
9176      */
9177     jsonRoot : "",
9178
9179     /**
9180      * Refreshes the view.
9181      */
9182     refresh : function(){
9183         this.clearSelections();
9184         this.el.update("");
9185         var html = [];
9186         var o = this.jsonData;
9187         if(o && o.length > 0){
9188             for(var i = 0, len = o.length; i < len; i++){
9189                 var data = this.prepareData(o[i], i, o);
9190                 html[html.length] = this.tpl.apply(data);
9191             }
9192         }else{
9193             html.push(this.emptyText);
9194         }
9195         this.el.update(html.join(""));
9196         this.nodes = this.el.dom.childNodes;
9197         this.updateIndexes(0);
9198     },
9199
9200     /**
9201      * 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.
9202      * @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:
9203      <pre><code>
9204      view.load({
9205          url: "your-url.php",
9206          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9207          callback: yourFunction,
9208          scope: yourObject, //(optional scope)
9209          discardUrl: false,
9210          nocache: false,
9211          text: "Loading...",
9212          timeout: 30,
9213          scripts: false
9214      });
9215      </code></pre>
9216      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9217      * 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.
9218      * @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}
9219      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9220      * @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.
9221      */
9222     load : function(){
9223         var um = this.el.getUpdateManager();
9224         um.update.apply(um, arguments);
9225     },
9226
9227     render : function(el, response){
9228         this.clearSelections();
9229         this.el.update("");
9230         var o;
9231         try{
9232             o = Roo.util.JSON.decode(response.responseText);
9233             if(this.jsonRoot){
9234                 
9235                 o = o[this.jsonRoot];
9236             }
9237         } catch(e){
9238         }
9239         /**
9240          * The current JSON data or null
9241          */
9242         this.jsonData = o;
9243         this.beforeRender();
9244         this.refresh();
9245     },
9246
9247 /**
9248  * Get the number of records in the current JSON dataset
9249  * @return {Number}
9250  */
9251     getCount : function(){
9252         return this.jsonData ? this.jsonData.length : 0;
9253     },
9254
9255 /**
9256  * Returns the JSON object for the specified node(s)
9257  * @param {HTMLElement/Array} node The node or an array of nodes
9258  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9259  * you get the JSON object for the node
9260  */
9261     getNodeData : function(node){
9262         if(node instanceof Array){
9263             var data = [];
9264             for(var i = 0, len = node.length; i < len; i++){
9265                 data.push(this.getNodeData(node[i]));
9266             }
9267             return data;
9268         }
9269         return this.jsonData[this.indexOf(node)] || null;
9270     },
9271
9272     beforeRender : function(){
9273         this.snapshot = this.jsonData;
9274         if(this.sortInfo){
9275             this.sort.apply(this, this.sortInfo);
9276         }
9277         this.fireEvent("beforerender", this, this.jsonData);
9278     },
9279
9280     onLoad : function(el, o){
9281         this.fireEvent("load", this, this.jsonData, o);
9282     },
9283
9284     onLoadException : function(el, o){
9285         this.fireEvent("loadexception", this, o);
9286     },
9287
9288 /**
9289  * Filter the data by a specific property.
9290  * @param {String} property A property on your JSON objects
9291  * @param {String/RegExp} value Either string that the property values
9292  * should start with, or a RegExp to test against the property
9293  */
9294     filter : function(property, value){
9295         if(this.jsonData){
9296             var data = [];
9297             var ss = this.snapshot;
9298             if(typeof value == "string"){
9299                 var vlen = value.length;
9300                 if(vlen == 0){
9301                     this.clearFilter();
9302                     return;
9303                 }
9304                 value = value.toLowerCase();
9305                 for(var i = 0, len = ss.length; i < len; i++){
9306                     var o = ss[i];
9307                     if(o[property].substr(0, vlen).toLowerCase() == value){
9308                         data.push(o);
9309                     }
9310                 }
9311             } else if(value.exec){ // regex?
9312                 for(var i = 0, len = ss.length; i < len; i++){
9313                     var o = ss[i];
9314                     if(value.test(o[property])){
9315                         data.push(o);
9316                     }
9317                 }
9318             } else{
9319                 return;
9320             }
9321             this.jsonData = data;
9322             this.refresh();
9323         }
9324     },
9325
9326 /**
9327  * Filter by a function. The passed function will be called with each
9328  * object in the current dataset. If the function returns true the value is kept,
9329  * otherwise it is filtered.
9330  * @param {Function} fn
9331  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9332  */
9333     filterBy : function(fn, scope){
9334         if(this.jsonData){
9335             var data = [];
9336             var ss = this.snapshot;
9337             for(var i = 0, len = ss.length; i < len; i++){
9338                 var o = ss[i];
9339                 if(fn.call(scope || this, o)){
9340                     data.push(o);
9341                 }
9342             }
9343             this.jsonData = data;
9344             this.refresh();
9345         }
9346     },
9347
9348 /**
9349  * Clears the current filter.
9350  */
9351     clearFilter : function(){
9352         if(this.snapshot && this.jsonData != this.snapshot){
9353             this.jsonData = this.snapshot;
9354             this.refresh();
9355         }
9356     },
9357
9358
9359 /**
9360  * Sorts the data for this view and refreshes it.
9361  * @param {String} property A property on your JSON objects to sort on
9362  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9363  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9364  */
9365     sort : function(property, dir, sortType){
9366         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9367         if(this.jsonData){
9368             var p = property;
9369             var dsc = dir && dir.toLowerCase() == "desc";
9370             var f = function(o1, o2){
9371                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9372                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9373                 ;
9374                 if(v1 < v2){
9375                     return dsc ? +1 : -1;
9376                 } else if(v1 > v2){
9377                     return dsc ? -1 : +1;
9378                 } else{
9379                     return 0;
9380                 }
9381             };
9382             this.jsonData.sort(f);
9383             this.refresh();
9384             if(this.jsonData != this.snapshot){
9385                 this.snapshot.sort(f);
9386             }
9387         }
9388     }
9389 });/*
9390  * Based on:
9391  * Ext JS Library 1.1.1
9392  * Copyright(c) 2006-2007, Ext JS, LLC.
9393  *
9394  * Originally Released Under LGPL - original licence link has changed is not relivant.
9395  *
9396  * Fork - LGPL
9397  * <script type="text/javascript">
9398  */
9399  
9400
9401 /**
9402  * @class Roo.ColorPalette
9403  * @extends Roo.Component
9404  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9405  * Here's an example of typical usage:
9406  * <pre><code>
9407 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9408 cp.render('my-div');
9409
9410 cp.on('select', function(palette, selColor){
9411     // do something with selColor
9412 });
9413 </code></pre>
9414  * @constructor
9415  * Create a new ColorPalette
9416  * @param {Object} config The config object
9417  */
9418 Roo.ColorPalette = function(config){
9419     Roo.ColorPalette.superclass.constructor.call(this, config);
9420     this.addEvents({
9421         /**
9422              * @event select
9423              * Fires when a color is selected
9424              * @param {ColorPalette} this
9425              * @param {String} color The 6-digit color hex code (without the # symbol)
9426              */
9427         select: true
9428     });
9429
9430     if(this.handler){
9431         this.on("select", this.handler, this.scope, true);
9432     }
9433 };
9434 Roo.extend(Roo.ColorPalette, Roo.Component, {
9435     /**
9436      * @cfg {String} itemCls
9437      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9438      */
9439     itemCls : "x-color-palette",
9440     /**
9441      * @cfg {String} value
9442      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9443      * the hex codes are case-sensitive.
9444      */
9445     value : null,
9446     clickEvent:'click',
9447     // private
9448     ctype: "Roo.ColorPalette",
9449
9450     /**
9451      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9452      */
9453     allowReselect : false,
9454
9455     /**
9456      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9457      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9458      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9459      * of colors with the width setting until the box is symmetrical.</p>
9460      * <p>You can override individual colors if needed:</p>
9461      * <pre><code>
9462 var cp = new Roo.ColorPalette();
9463 cp.colors[0] = "FF0000";  // change the first box to red
9464 </code></pre>
9465
9466 Or you can provide a custom array of your own for complete control:
9467 <pre><code>
9468 var cp = new Roo.ColorPalette();
9469 cp.colors = ["000000", "993300", "333300"];
9470 </code></pre>
9471      * @type Array
9472      */
9473     colors : [
9474         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9475         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9476         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9477         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9478         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9479     ],
9480
9481     // private
9482     onRender : function(container, position){
9483         var t = new Roo.MasterTemplate(
9484             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9485         );
9486         var c = this.colors;
9487         for(var i = 0, len = c.length; i < len; i++){
9488             t.add([c[i]]);
9489         }
9490         var el = document.createElement("div");
9491         el.className = this.itemCls;
9492         t.overwrite(el);
9493         container.dom.insertBefore(el, position);
9494         this.el = Roo.get(el);
9495         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9496         if(this.clickEvent != 'click'){
9497             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9498         }
9499     },
9500
9501     // private
9502     afterRender : function(){
9503         Roo.ColorPalette.superclass.afterRender.call(this);
9504         if(this.value){
9505             var s = this.value;
9506             this.value = null;
9507             this.select(s);
9508         }
9509     },
9510
9511     // private
9512     handleClick : function(e, t){
9513         e.preventDefault();
9514         if(!this.disabled){
9515             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9516             this.select(c.toUpperCase());
9517         }
9518     },
9519
9520     /**
9521      * Selects the specified color in the palette (fires the select event)
9522      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9523      */
9524     select : function(color){
9525         color = color.replace("#", "");
9526         if(color != this.value || this.allowReselect){
9527             var el = this.el;
9528             if(this.value){
9529                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9530             }
9531             el.child("a.color-"+color).addClass("x-color-palette-sel");
9532             this.value = color;
9533             this.fireEvent("select", this, color);
9534         }
9535     }
9536 });/*
9537  * Based on:
9538  * Ext JS Library 1.1.1
9539  * Copyright(c) 2006-2007, Ext JS, LLC.
9540  *
9541  * Originally Released Under LGPL - original licence link has changed is not relivant.
9542  *
9543  * Fork - LGPL
9544  * <script type="text/javascript">
9545  */
9546  
9547 /**
9548  * @class Roo.DatePicker
9549  * @extends Roo.Component
9550  * Simple date picker class.
9551  * @constructor
9552  * Create a new DatePicker
9553  * @param {Object} config The config object
9554  */
9555 Roo.DatePicker = function(config){
9556     Roo.DatePicker.superclass.constructor.call(this, config);
9557
9558     this.value = config && config.value ?
9559                  config.value.clearTime() : new Date().clearTime();
9560
9561     this.addEvents({
9562         /**
9563              * @event select
9564              * Fires when a date is selected
9565              * @param {DatePicker} this
9566              * @param {Date} date The selected date
9567              */
9568         'select': true,
9569         /**
9570              * @event monthchange
9571              * Fires when the displayed month changes 
9572              * @param {DatePicker} this
9573              * @param {Date} date The selected month
9574              */
9575         'monthchange': true
9576     });
9577
9578     if(this.handler){
9579         this.on("select", this.handler,  this.scope || this);
9580     }
9581     // build the disabledDatesRE
9582     if(!this.disabledDatesRE && this.disabledDates){
9583         var dd = this.disabledDates;
9584         var re = "(?:";
9585         for(var i = 0; i < dd.length; i++){
9586             re += dd[i];
9587             if(i != dd.length-1) re += "|";
9588         }
9589         this.disabledDatesRE = new RegExp(re + ")");
9590     }
9591 };
9592
9593 Roo.extend(Roo.DatePicker, Roo.Component, {
9594     /**
9595      * @cfg {String} todayText
9596      * The text to display on the button that selects the current date (defaults to "Today")
9597      */
9598     todayText : "Today",
9599     /**
9600      * @cfg {String} okText
9601      * The text to display on the ok button
9602      */
9603     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9604     /**
9605      * @cfg {String} cancelText
9606      * The text to display on the cancel button
9607      */
9608     cancelText : "Cancel",
9609     /**
9610      * @cfg {String} todayTip
9611      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9612      */
9613     todayTip : "{0} (Spacebar)",
9614     /**
9615      * @cfg {Date} minDate
9616      * Minimum allowable date (JavaScript date object, defaults to null)
9617      */
9618     minDate : null,
9619     /**
9620      * @cfg {Date} maxDate
9621      * Maximum allowable date (JavaScript date object, defaults to null)
9622      */
9623     maxDate : null,
9624     /**
9625      * @cfg {String} minText
9626      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9627      */
9628     minText : "This date is before the minimum date",
9629     /**
9630      * @cfg {String} maxText
9631      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9632      */
9633     maxText : "This date is after the maximum date",
9634     /**
9635      * @cfg {String} format
9636      * The default date format string which can be overriden for localization support.  The format must be
9637      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9638      */
9639     format : "m/d/y",
9640     /**
9641      * @cfg {Array} disabledDays
9642      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9643      */
9644     disabledDays : null,
9645     /**
9646      * @cfg {String} disabledDaysText
9647      * The tooltip to display when the date falls on a disabled day (defaults to "")
9648      */
9649     disabledDaysText : "",
9650     /**
9651      * @cfg {RegExp} disabledDatesRE
9652      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9653      */
9654     disabledDatesRE : null,
9655     /**
9656      * @cfg {String} disabledDatesText
9657      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9658      */
9659     disabledDatesText : "",
9660     /**
9661      * @cfg {Boolean} constrainToViewport
9662      * True to constrain the date picker to the viewport (defaults to true)
9663      */
9664     constrainToViewport : true,
9665     /**
9666      * @cfg {Array} monthNames
9667      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9668      */
9669     monthNames : Date.monthNames,
9670     /**
9671      * @cfg {Array} dayNames
9672      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9673      */
9674     dayNames : Date.dayNames,
9675     /**
9676      * @cfg {String} nextText
9677      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9678      */
9679     nextText: 'Next Month (Control+Right)',
9680     /**
9681      * @cfg {String} prevText
9682      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9683      */
9684     prevText: 'Previous Month (Control+Left)',
9685     /**
9686      * @cfg {String} monthYearText
9687      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9688      */
9689     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9690     /**
9691      * @cfg {Number} startDay
9692      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9693      */
9694     startDay : 0,
9695     /**
9696      * @cfg {Bool} showClear
9697      * Show a clear button (usefull for date form elements that can be blank.)
9698      */
9699     
9700     showClear: false,
9701     
9702     /**
9703      * Sets the value of the date field
9704      * @param {Date} value The date to set
9705      */
9706     setValue : function(value){
9707         var old = this.value;
9708         
9709         if (typeof(value) == 'string') {
9710          
9711             value = Date.parseDate(value, this.format);
9712         }
9713         if (!value) {
9714             value = new Date();
9715         }
9716         
9717         this.value = value.clearTime(true);
9718         if(this.el){
9719             this.update(this.value);
9720         }
9721     },
9722
9723     /**
9724      * Gets the current selected value of the date field
9725      * @return {Date} The selected date
9726      */
9727     getValue : function(){
9728         return this.value;
9729     },
9730
9731     // private
9732     focus : function(){
9733         if(this.el){
9734             this.update(this.activeDate);
9735         }
9736     },
9737
9738     // privateval
9739     onRender : function(container, position){
9740         
9741         var m = [
9742              '<table cellspacing="0">',
9743                 '<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>',
9744                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9745         var dn = this.dayNames;
9746         for(var i = 0; i < 7; i++){
9747             var d = this.startDay+i;
9748             if(d > 6){
9749                 d = d-7;
9750             }
9751             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9752         }
9753         m[m.length] = "</tr></thead><tbody><tr>";
9754         for(var i = 0; i < 42; i++) {
9755             if(i % 7 == 0 && i != 0){
9756                 m[m.length] = "</tr><tr>";
9757             }
9758             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9759         }
9760         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9761             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9762
9763         var el = document.createElement("div");
9764         el.className = "x-date-picker";
9765         el.innerHTML = m.join("");
9766
9767         container.dom.insertBefore(el, position);
9768
9769         this.el = Roo.get(el);
9770         this.eventEl = Roo.get(el.firstChild);
9771
9772         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9773             handler: this.showPrevMonth,
9774             scope: this,
9775             preventDefault:true,
9776             stopDefault:true
9777         });
9778
9779         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9780             handler: this.showNextMonth,
9781             scope: this,
9782             preventDefault:true,
9783             stopDefault:true
9784         });
9785
9786         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9787
9788         this.monthPicker = this.el.down('div.x-date-mp');
9789         this.monthPicker.enableDisplayMode('block');
9790         
9791         var kn = new Roo.KeyNav(this.eventEl, {
9792             "left" : function(e){
9793                 e.ctrlKey ?
9794                     this.showPrevMonth() :
9795                     this.update(this.activeDate.add("d", -1));
9796             },
9797
9798             "right" : function(e){
9799                 e.ctrlKey ?
9800                     this.showNextMonth() :
9801                     this.update(this.activeDate.add("d", 1));
9802             },
9803
9804             "up" : function(e){
9805                 e.ctrlKey ?
9806                     this.showNextYear() :
9807                     this.update(this.activeDate.add("d", -7));
9808             },
9809
9810             "down" : function(e){
9811                 e.ctrlKey ?
9812                     this.showPrevYear() :
9813                     this.update(this.activeDate.add("d", 7));
9814             },
9815
9816             "pageUp" : function(e){
9817                 this.showNextMonth();
9818             },
9819
9820             "pageDown" : function(e){
9821                 this.showPrevMonth();
9822             },
9823
9824             "enter" : function(e){
9825                 e.stopPropagation();
9826                 return true;
9827             },
9828
9829             scope : this
9830         });
9831
9832         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9833
9834         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9835
9836         this.el.unselectable();
9837         
9838         this.cells = this.el.select("table.x-date-inner tbody td");
9839         this.textNodes = this.el.query("table.x-date-inner tbody span");
9840
9841         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9842             text: "&#160;",
9843             tooltip: this.monthYearText
9844         });
9845
9846         this.mbtn.on('click', this.showMonthPicker, this);
9847         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9848
9849
9850         var today = (new Date()).dateFormat(this.format);
9851         
9852         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9853         if (this.showClear) {
9854             baseTb.add( new Roo.Toolbar.Fill());
9855         }
9856         baseTb.add({
9857             text: String.format(this.todayText, today),
9858             tooltip: String.format(this.todayTip, today),
9859             handler: this.selectToday,
9860             scope: this
9861         });
9862         
9863         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9864             
9865         //});
9866         if (this.showClear) {
9867             
9868             baseTb.add( new Roo.Toolbar.Fill());
9869             baseTb.add({
9870                 text: '&#160;',
9871                 cls: 'x-btn-icon x-btn-clear',
9872                 handler: function() {
9873                     //this.value = '';
9874                     this.fireEvent("select", this, '');
9875                 },
9876                 scope: this
9877             });
9878         }
9879         
9880         
9881         if(Roo.isIE){
9882             this.el.repaint();
9883         }
9884         this.update(this.value);
9885     },
9886
9887     createMonthPicker : function(){
9888         if(!this.monthPicker.dom.firstChild){
9889             var buf = ['<table border="0" cellspacing="0">'];
9890             for(var i = 0; i < 6; i++){
9891                 buf.push(
9892                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9893                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9894                     i == 0 ?
9895                     '<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>' :
9896                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9897                 );
9898             }
9899             buf.push(
9900                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9901                     this.okText,
9902                     '</button><button type="button" class="x-date-mp-cancel">',
9903                     this.cancelText,
9904                     '</button></td></tr>',
9905                 '</table>'
9906             );
9907             this.monthPicker.update(buf.join(''));
9908             this.monthPicker.on('click', this.onMonthClick, this);
9909             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9910
9911             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9912             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9913
9914             this.mpMonths.each(function(m, a, i){
9915                 i += 1;
9916                 if((i%2) == 0){
9917                     m.dom.xmonth = 5 + Math.round(i * .5);
9918                 }else{
9919                     m.dom.xmonth = Math.round((i-1) * .5);
9920                 }
9921             });
9922         }
9923     },
9924
9925     showMonthPicker : function(){
9926         this.createMonthPicker();
9927         var size = this.el.getSize();
9928         this.monthPicker.setSize(size);
9929         this.monthPicker.child('table').setSize(size);
9930
9931         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9932         this.updateMPMonth(this.mpSelMonth);
9933         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9934         this.updateMPYear(this.mpSelYear);
9935
9936         this.monthPicker.slideIn('t', {duration:.2});
9937     },
9938
9939     updateMPYear : function(y){
9940         this.mpyear = y;
9941         var ys = this.mpYears.elements;
9942         for(var i = 1; i <= 10; i++){
9943             var td = ys[i-1], y2;
9944             if((i%2) == 0){
9945                 y2 = y + Math.round(i * .5);
9946                 td.firstChild.innerHTML = y2;
9947                 td.xyear = y2;
9948             }else{
9949                 y2 = y - (5-Math.round(i * .5));
9950                 td.firstChild.innerHTML = y2;
9951                 td.xyear = y2;
9952             }
9953             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9954         }
9955     },
9956
9957     updateMPMonth : function(sm){
9958         this.mpMonths.each(function(m, a, i){
9959             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9960         });
9961     },
9962
9963     selectMPMonth: function(m){
9964         
9965     },
9966
9967     onMonthClick : function(e, t){
9968         e.stopEvent();
9969         var el = new Roo.Element(t), pn;
9970         if(el.is('button.x-date-mp-cancel')){
9971             this.hideMonthPicker();
9972         }
9973         else if(el.is('button.x-date-mp-ok')){
9974             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9975             this.hideMonthPicker();
9976         }
9977         else if(pn = el.up('td.x-date-mp-month', 2)){
9978             this.mpMonths.removeClass('x-date-mp-sel');
9979             pn.addClass('x-date-mp-sel');
9980             this.mpSelMonth = pn.dom.xmonth;
9981         }
9982         else if(pn = el.up('td.x-date-mp-year', 2)){
9983             this.mpYears.removeClass('x-date-mp-sel');
9984             pn.addClass('x-date-mp-sel');
9985             this.mpSelYear = pn.dom.xyear;
9986         }
9987         else if(el.is('a.x-date-mp-prev')){
9988             this.updateMPYear(this.mpyear-10);
9989         }
9990         else if(el.is('a.x-date-mp-next')){
9991             this.updateMPYear(this.mpyear+10);
9992         }
9993     },
9994
9995     onMonthDblClick : function(e, t){
9996         e.stopEvent();
9997         var el = new Roo.Element(t), pn;
9998         if(pn = el.up('td.x-date-mp-month', 2)){
9999             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10000             this.hideMonthPicker();
10001         }
10002         else if(pn = el.up('td.x-date-mp-year', 2)){
10003             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10004             this.hideMonthPicker();
10005         }
10006     },
10007
10008     hideMonthPicker : function(disableAnim){
10009         if(this.monthPicker){
10010             if(disableAnim === true){
10011                 this.monthPicker.hide();
10012             }else{
10013                 this.monthPicker.slideOut('t', {duration:.2});
10014             }
10015         }
10016     },
10017
10018     // private
10019     showPrevMonth : function(e){
10020         this.update(this.activeDate.add("mo", -1));
10021     },
10022
10023     // private
10024     showNextMonth : function(e){
10025         this.update(this.activeDate.add("mo", 1));
10026     },
10027
10028     // private
10029     showPrevYear : function(){
10030         this.update(this.activeDate.add("y", -1));
10031     },
10032
10033     // private
10034     showNextYear : function(){
10035         this.update(this.activeDate.add("y", 1));
10036     },
10037
10038     // private
10039     handleMouseWheel : function(e){
10040         var delta = e.getWheelDelta();
10041         if(delta > 0){
10042             this.showPrevMonth();
10043             e.stopEvent();
10044         } else if(delta < 0){
10045             this.showNextMonth();
10046             e.stopEvent();
10047         }
10048     },
10049
10050     // private
10051     handleDateClick : function(e, t){
10052         e.stopEvent();
10053         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10054             this.setValue(new Date(t.dateValue));
10055             this.fireEvent("select", this, this.value);
10056         }
10057     },
10058
10059     // private
10060     selectToday : function(){
10061         this.setValue(new Date().clearTime());
10062         this.fireEvent("select", this, this.value);
10063     },
10064
10065     // private
10066     update : function(date)
10067     {
10068         var vd = this.activeDate;
10069         this.activeDate = date;
10070         if(vd && this.el){
10071             var t = date.getTime();
10072             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10073                 this.cells.removeClass("x-date-selected");
10074                 this.cells.each(function(c){
10075                    if(c.dom.firstChild.dateValue == t){
10076                        c.addClass("x-date-selected");
10077                        setTimeout(function(){
10078                             try{c.dom.firstChild.focus();}catch(e){}
10079                        }, 50);
10080                        return false;
10081                    }
10082                 });
10083                 return;
10084             }
10085         }
10086         
10087         var days = date.getDaysInMonth();
10088         var firstOfMonth = date.getFirstDateOfMonth();
10089         var startingPos = firstOfMonth.getDay()-this.startDay;
10090
10091         if(startingPos <= this.startDay){
10092             startingPos += 7;
10093         }
10094
10095         var pm = date.add("mo", -1);
10096         var prevStart = pm.getDaysInMonth()-startingPos;
10097
10098         var cells = this.cells.elements;
10099         var textEls = this.textNodes;
10100         days += startingPos;
10101
10102         // convert everything to numbers so it's fast
10103         var day = 86400000;
10104         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10105         var today = new Date().clearTime().getTime();
10106         var sel = date.clearTime().getTime();
10107         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10108         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10109         var ddMatch = this.disabledDatesRE;
10110         var ddText = this.disabledDatesText;
10111         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10112         var ddaysText = this.disabledDaysText;
10113         var format = this.format;
10114
10115         var setCellClass = function(cal, cell){
10116             cell.title = "";
10117             var t = d.getTime();
10118             cell.firstChild.dateValue = t;
10119             if(t == today){
10120                 cell.className += " x-date-today";
10121                 cell.title = cal.todayText;
10122             }
10123             if(t == sel){
10124                 cell.className += " x-date-selected";
10125                 setTimeout(function(){
10126                     try{cell.firstChild.focus();}catch(e){}
10127                 }, 50);
10128             }
10129             // disabling
10130             if(t < min) {
10131                 cell.className = " x-date-disabled";
10132                 cell.title = cal.minText;
10133                 return;
10134             }
10135             if(t > max) {
10136                 cell.className = " x-date-disabled";
10137                 cell.title = cal.maxText;
10138                 return;
10139             }
10140             if(ddays){
10141                 if(ddays.indexOf(d.getDay()) != -1){
10142                     cell.title = ddaysText;
10143                     cell.className = " x-date-disabled";
10144                 }
10145             }
10146             if(ddMatch && format){
10147                 var fvalue = d.dateFormat(format);
10148                 if(ddMatch.test(fvalue)){
10149                     cell.title = ddText.replace("%0", fvalue);
10150                     cell.className = " x-date-disabled";
10151                 }
10152             }
10153         };
10154
10155         var i = 0;
10156         for(; i < startingPos; i++) {
10157             textEls[i].innerHTML = (++prevStart);
10158             d.setDate(d.getDate()+1);
10159             cells[i].className = "x-date-prevday";
10160             setCellClass(this, cells[i]);
10161         }
10162         for(; i < days; i++){
10163             intDay = i - startingPos + 1;
10164             textEls[i].innerHTML = (intDay);
10165             d.setDate(d.getDate()+1);
10166             cells[i].className = "x-date-active";
10167             setCellClass(this, cells[i]);
10168         }
10169         var extraDays = 0;
10170         for(; i < 42; i++) {
10171              textEls[i].innerHTML = (++extraDays);
10172              d.setDate(d.getDate()+1);
10173              cells[i].className = "x-date-nextday";
10174              setCellClass(this, cells[i]);
10175         }
10176
10177         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10178         this.fireEvent('monthchange', this, date);
10179         
10180         if(!this.internalRender){
10181             var main = this.el.dom.firstChild;
10182             var w = main.offsetWidth;
10183             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10184             Roo.fly(main).setWidth(w);
10185             this.internalRender = true;
10186             // opera does not respect the auto grow header center column
10187             // then, after it gets a width opera refuses to recalculate
10188             // without a second pass
10189             if(Roo.isOpera && !this.secondPass){
10190                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10191                 this.secondPass = true;
10192                 this.update.defer(10, this, [date]);
10193             }
10194         }
10195         
10196         
10197     }
10198 });        /*
10199  * Based on:
10200  * Ext JS Library 1.1.1
10201  * Copyright(c) 2006-2007, Ext JS, LLC.
10202  *
10203  * Originally Released Under LGPL - original licence link has changed is not relivant.
10204  *
10205  * Fork - LGPL
10206  * <script type="text/javascript">
10207  */
10208 /**
10209  * @class Roo.TabPanel
10210  * @extends Roo.util.Observable
10211  * A lightweight tab container.
10212  * <br><br>
10213  * Usage:
10214  * <pre><code>
10215 // basic tabs 1, built from existing content
10216 var tabs = new Roo.TabPanel("tabs1");
10217 tabs.addTab("script", "View Script");
10218 tabs.addTab("markup", "View Markup");
10219 tabs.activate("script");
10220
10221 // more advanced tabs, built from javascript
10222 var jtabs = new Roo.TabPanel("jtabs");
10223 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10224
10225 // set up the UpdateManager
10226 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10227 var updater = tab2.getUpdateManager();
10228 updater.setDefaultUrl("ajax1.htm");
10229 tab2.on('activate', updater.refresh, updater, true);
10230
10231 // Use setUrl for Ajax loading
10232 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10233 tab3.setUrl("ajax2.htm", null, true);
10234
10235 // Disabled tab
10236 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10237 tab4.disable();
10238
10239 jtabs.activate("jtabs-1");
10240  * </code></pre>
10241  * @constructor
10242  * Create a new TabPanel.
10243  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10244  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10245  */
10246 Roo.TabPanel = function(container, config){
10247     /**
10248     * The container element for this TabPanel.
10249     * @type Roo.Element
10250     */
10251     this.el = Roo.get(container, true);
10252     if(config){
10253         if(typeof config == "boolean"){
10254             this.tabPosition = config ? "bottom" : "top";
10255         }else{
10256             Roo.apply(this, config);
10257         }
10258     }
10259     if(this.tabPosition == "bottom"){
10260         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10261         this.el.addClass("x-tabs-bottom");
10262     }
10263     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10264     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10265     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10266     if(Roo.isIE){
10267         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10268     }
10269     if(this.tabPosition != "bottom"){
10270         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10271          * @type Roo.Element
10272          */
10273         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10274         this.el.addClass("x-tabs-top");
10275     }
10276     this.items = [];
10277
10278     this.bodyEl.setStyle("position", "relative");
10279
10280     this.active = null;
10281     this.activateDelegate = this.activate.createDelegate(this);
10282
10283     this.addEvents({
10284         /**
10285          * @event tabchange
10286          * Fires when the active tab changes
10287          * @param {Roo.TabPanel} this
10288          * @param {Roo.TabPanelItem} activePanel The new active tab
10289          */
10290         "tabchange": true,
10291         /**
10292          * @event beforetabchange
10293          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10294          * @param {Roo.TabPanel} this
10295          * @param {Object} e Set cancel to true on this object to cancel the tab change
10296          * @param {Roo.TabPanelItem} tab The tab being changed to
10297          */
10298         "beforetabchange" : true
10299     });
10300
10301     Roo.EventManager.onWindowResize(this.onResize, this);
10302     this.cpad = this.el.getPadding("lr");
10303     this.hiddenCount = 0;
10304
10305
10306     // toolbar on the tabbar support...
10307     if (this.toolbar) {
10308         var tcfg = this.toolbar;
10309         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10310         this.toolbar = new Roo.Toolbar(tcfg);
10311         if (Roo.isSafari) {
10312             var tbl = tcfg.container.child('table', true);
10313             tbl.setAttribute('width', '100%');
10314         }
10315         
10316     }
10317    
10318
10319
10320     Roo.TabPanel.superclass.constructor.call(this);
10321 };
10322
10323 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10324     /*
10325      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10326      */
10327     tabPosition : "top",
10328     /*
10329      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10330      */
10331     currentTabWidth : 0,
10332     /*
10333      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10334      */
10335     minTabWidth : 40,
10336     /*
10337      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10338      */
10339     maxTabWidth : 250,
10340     /*
10341      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10342      */
10343     preferredTabWidth : 175,
10344     /*
10345      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10346      */
10347     resizeTabs : false,
10348     /*
10349      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10350      */
10351     monitorResize : true,
10352     /*
10353      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10354      */
10355     toolbar : false,
10356
10357     /**
10358      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10359      * @param {String} id The id of the div to use <b>or create</b>
10360      * @param {String} text The text for the tab
10361      * @param {String} content (optional) Content to put in the TabPanelItem body
10362      * @param {Boolean} closable (optional) True to create a close icon on the tab
10363      * @return {Roo.TabPanelItem} The created TabPanelItem
10364      */
10365     addTab : function(id, text, content, closable){
10366         var item = new Roo.TabPanelItem(this, id, text, closable);
10367         this.addTabItem(item);
10368         if(content){
10369             item.setContent(content);
10370         }
10371         return item;
10372     },
10373
10374     /**
10375      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10376      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10377      * @return {Roo.TabPanelItem}
10378      */
10379     getTab : function(id){
10380         return this.items[id];
10381     },
10382
10383     /**
10384      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10385      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10386      */
10387     hideTab : function(id){
10388         var t = this.items[id];
10389         if(!t.isHidden()){
10390            t.setHidden(true);
10391            this.hiddenCount++;
10392            this.autoSizeTabs();
10393         }
10394     },
10395
10396     /**
10397      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10398      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10399      */
10400     unhideTab : function(id){
10401         var t = this.items[id];
10402         if(t.isHidden()){
10403            t.setHidden(false);
10404            this.hiddenCount--;
10405            this.autoSizeTabs();
10406         }
10407     },
10408
10409     /**
10410      * Adds an existing {@link Roo.TabPanelItem}.
10411      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10412      */
10413     addTabItem : function(item){
10414         this.items[item.id] = item;
10415         this.items.push(item);
10416         if(this.resizeTabs){
10417            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10418            this.autoSizeTabs();
10419         }else{
10420             item.autoSize();
10421         }
10422     },
10423
10424     /**
10425      * Removes a {@link Roo.TabPanelItem}.
10426      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10427      */
10428     removeTab : function(id){
10429         var items = this.items;
10430         var tab = items[id];
10431         if(!tab) { return; }
10432         var index = items.indexOf(tab);
10433         if(this.active == tab && items.length > 1){
10434             var newTab = this.getNextAvailable(index);
10435             if(newTab) {
10436                 newTab.activate();
10437             }
10438         }
10439         this.stripEl.dom.removeChild(tab.pnode.dom);
10440         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10441             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10442         }
10443         items.splice(index, 1);
10444         delete this.items[tab.id];
10445         tab.fireEvent("close", tab);
10446         tab.purgeListeners();
10447         this.autoSizeTabs();
10448     },
10449
10450     getNextAvailable : function(start){
10451         var items = this.items;
10452         var index = start;
10453         // look for a next tab that will slide over to
10454         // replace the one being removed
10455         while(index < items.length){
10456             var item = items[++index];
10457             if(item && !item.isHidden()){
10458                 return item;
10459             }
10460         }
10461         // if one isn't found select the previous tab (on the left)
10462         index = start;
10463         while(index >= 0){
10464             var item = items[--index];
10465             if(item && !item.isHidden()){
10466                 return item;
10467             }
10468         }
10469         return null;
10470     },
10471
10472     /**
10473      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10474      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10475      */
10476     disableTab : function(id){
10477         var tab = this.items[id];
10478         if(tab && this.active != tab){
10479             tab.disable();
10480         }
10481     },
10482
10483     /**
10484      * Enables a {@link Roo.TabPanelItem} that is disabled.
10485      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10486      */
10487     enableTab : function(id){
10488         var tab = this.items[id];
10489         tab.enable();
10490     },
10491
10492     /**
10493      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10494      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10495      * @return {Roo.TabPanelItem} The TabPanelItem.
10496      */
10497     activate : function(id){
10498         var tab = this.items[id];
10499         if(!tab){
10500             return null;
10501         }
10502         if(tab == this.active || tab.disabled){
10503             return tab;
10504         }
10505         var e = {};
10506         this.fireEvent("beforetabchange", this, e, tab);
10507         if(e.cancel !== true && !tab.disabled){
10508             if(this.active){
10509                 this.active.hide();
10510             }
10511             this.active = this.items[id];
10512             this.active.show();
10513             this.fireEvent("tabchange", this, this.active);
10514         }
10515         return tab;
10516     },
10517
10518     /**
10519      * Gets the active {@link Roo.TabPanelItem}.
10520      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10521      */
10522     getActiveTab : function(){
10523         return this.active;
10524     },
10525
10526     /**
10527      * Updates the tab body element to fit the height of the container element
10528      * for overflow scrolling
10529      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10530      */
10531     syncHeight : function(targetHeight){
10532         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10533         var bm = this.bodyEl.getMargins();
10534         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10535         this.bodyEl.setHeight(newHeight);
10536         return newHeight;
10537     },
10538
10539     onResize : function(){
10540         if(this.monitorResize){
10541             this.autoSizeTabs();
10542         }
10543     },
10544
10545     /**
10546      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10547      */
10548     beginUpdate : function(){
10549         this.updating = true;
10550     },
10551
10552     /**
10553      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10554      */
10555     endUpdate : function(){
10556         this.updating = false;
10557         this.autoSizeTabs();
10558     },
10559
10560     /**
10561      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10562      */
10563     autoSizeTabs : function(){
10564         var count = this.items.length;
10565         var vcount = count - this.hiddenCount;
10566         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10567         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10568         var availWidth = Math.floor(w / vcount);
10569         var b = this.stripBody;
10570         if(b.getWidth() > w){
10571             var tabs = this.items;
10572             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10573             if(availWidth < this.minTabWidth){
10574                 /*if(!this.sleft){    // incomplete scrolling code
10575                     this.createScrollButtons();
10576                 }
10577                 this.showScroll();
10578                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10579             }
10580         }else{
10581             if(this.currentTabWidth < this.preferredTabWidth){
10582                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10583             }
10584         }
10585     },
10586
10587     /**
10588      * Returns the number of tabs in this TabPanel.
10589      * @return {Number}
10590      */
10591      getCount : function(){
10592          return this.items.length;
10593      },
10594
10595     /**
10596      * Resizes all the tabs to the passed width
10597      * @param {Number} The new width
10598      */
10599     setTabWidth : function(width){
10600         this.currentTabWidth = width;
10601         for(var i = 0, len = this.items.length; i < len; i++) {
10602                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10603         }
10604     },
10605
10606     /**
10607      * Destroys this TabPanel
10608      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10609      */
10610     destroy : function(removeEl){
10611         Roo.EventManager.removeResizeListener(this.onResize, this);
10612         for(var i = 0, len = this.items.length; i < len; i++){
10613             this.items[i].purgeListeners();
10614         }
10615         if(removeEl === true){
10616             this.el.update("");
10617             this.el.remove();
10618         }
10619     }
10620 });
10621
10622 /**
10623  * @class Roo.TabPanelItem
10624  * @extends Roo.util.Observable
10625  * Represents an individual item (tab plus body) in a TabPanel.
10626  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10627  * @param {String} id The id of this TabPanelItem
10628  * @param {String} text The text for the tab of this TabPanelItem
10629  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10630  */
10631 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10632     /**
10633      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10634      * @type Roo.TabPanel
10635      */
10636     this.tabPanel = tabPanel;
10637     /**
10638      * The id for this TabPanelItem
10639      * @type String
10640      */
10641     this.id = id;
10642     /** @private */
10643     this.disabled = false;
10644     /** @private */
10645     this.text = text;
10646     /** @private */
10647     this.loaded = false;
10648     this.closable = closable;
10649
10650     /**
10651      * The body element for this TabPanelItem.
10652      * @type Roo.Element
10653      */
10654     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10655     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10656     this.bodyEl.setStyle("display", "block");
10657     this.bodyEl.setStyle("zoom", "1");
10658     this.hideAction();
10659
10660     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10661     /** @private */
10662     this.el = Roo.get(els.el, true);
10663     this.inner = Roo.get(els.inner, true);
10664     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10665     this.pnode = Roo.get(els.el.parentNode, true);
10666     this.el.on("mousedown", this.onTabMouseDown, this);
10667     this.el.on("click", this.onTabClick, this);
10668     /** @private */
10669     if(closable){
10670         var c = Roo.get(els.close, true);
10671         c.dom.title = this.closeText;
10672         c.addClassOnOver("close-over");
10673         c.on("click", this.closeClick, this);
10674      }
10675
10676     this.addEvents({
10677          /**
10678          * @event activate
10679          * Fires when this tab becomes the active tab.
10680          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10681          * @param {Roo.TabPanelItem} this
10682          */
10683         "activate": true,
10684         /**
10685          * @event beforeclose
10686          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10687          * @param {Roo.TabPanelItem} this
10688          * @param {Object} e Set cancel to true on this object to cancel the close.
10689          */
10690         "beforeclose": true,
10691         /**
10692          * @event close
10693          * Fires when this tab is closed.
10694          * @param {Roo.TabPanelItem} this
10695          */
10696          "close": true,
10697         /**
10698          * @event deactivate
10699          * Fires when this tab is no longer the active tab.
10700          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10701          * @param {Roo.TabPanelItem} this
10702          */
10703          "deactivate" : true
10704     });
10705     this.hidden = false;
10706
10707     Roo.TabPanelItem.superclass.constructor.call(this);
10708 };
10709
10710 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10711     purgeListeners : function(){
10712        Roo.util.Observable.prototype.purgeListeners.call(this);
10713        this.el.removeAllListeners();
10714     },
10715     /**
10716      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10717      */
10718     show : function(){
10719         this.pnode.addClass("on");
10720         this.showAction();
10721         if(Roo.isOpera){
10722             this.tabPanel.stripWrap.repaint();
10723         }
10724         this.fireEvent("activate", this.tabPanel, this);
10725     },
10726
10727     /**
10728      * Returns true if this tab is the active tab.
10729      * @return {Boolean}
10730      */
10731     isActive : function(){
10732         return this.tabPanel.getActiveTab() == this;
10733     },
10734
10735     /**
10736      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10737      */
10738     hide : function(){
10739         this.pnode.removeClass("on");
10740         this.hideAction();
10741         this.fireEvent("deactivate", this.tabPanel, this);
10742     },
10743
10744     hideAction : function(){
10745         this.bodyEl.hide();
10746         this.bodyEl.setStyle("position", "absolute");
10747         this.bodyEl.setLeft("-20000px");
10748         this.bodyEl.setTop("-20000px");
10749     },
10750
10751     showAction : function(){
10752         this.bodyEl.setStyle("position", "relative");
10753         this.bodyEl.setTop("");
10754         this.bodyEl.setLeft("");
10755         this.bodyEl.show();
10756     },
10757
10758     /**
10759      * Set the tooltip for the tab.
10760      * @param {String} tooltip The tab's tooltip
10761      */
10762     setTooltip : function(text){
10763         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10764             this.textEl.dom.qtip = text;
10765             this.textEl.dom.removeAttribute('title');
10766         }else{
10767             this.textEl.dom.title = text;
10768         }
10769     },
10770
10771     onTabClick : function(e){
10772         e.preventDefault();
10773         this.tabPanel.activate(this.id);
10774     },
10775
10776     onTabMouseDown : function(e){
10777         e.preventDefault();
10778         this.tabPanel.activate(this.id);
10779     },
10780
10781     getWidth : function(){
10782         return this.inner.getWidth();
10783     },
10784
10785     setWidth : function(width){
10786         var iwidth = width - this.pnode.getPadding("lr");
10787         this.inner.setWidth(iwidth);
10788         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10789         this.pnode.setWidth(width);
10790     },
10791
10792     /**
10793      * Show or hide the tab
10794      * @param {Boolean} hidden True to hide or false to show.
10795      */
10796     setHidden : function(hidden){
10797         this.hidden = hidden;
10798         this.pnode.setStyle("display", hidden ? "none" : "");
10799     },
10800
10801     /**
10802      * Returns true if this tab is "hidden"
10803      * @return {Boolean}
10804      */
10805     isHidden : function(){
10806         return this.hidden;
10807     },
10808
10809     /**
10810      * Returns the text for this tab
10811      * @return {String}
10812      */
10813     getText : function(){
10814         return this.text;
10815     },
10816
10817     autoSize : function(){
10818         //this.el.beginMeasure();
10819         this.textEl.setWidth(1);
10820         /*
10821          *  #2804 [new] Tabs in Roojs
10822          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10823          */
10824         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10825         //this.el.endMeasure();
10826     },
10827
10828     /**
10829      * Sets the text for the tab (Note: this also sets the tooltip text)
10830      * @param {String} text The tab's text and tooltip
10831      */
10832     setText : function(text){
10833         this.text = text;
10834         this.textEl.update(text);
10835         this.setTooltip(text);
10836         if(!this.tabPanel.resizeTabs){
10837             this.autoSize();
10838         }
10839     },
10840     /**
10841      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10842      */
10843     activate : function(){
10844         this.tabPanel.activate(this.id);
10845     },
10846
10847     /**
10848      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10849      */
10850     disable : function(){
10851         if(this.tabPanel.active != this){
10852             this.disabled = true;
10853             this.pnode.addClass("disabled");
10854         }
10855     },
10856
10857     /**
10858      * Enables this TabPanelItem if it was previously disabled.
10859      */
10860     enable : function(){
10861         this.disabled = false;
10862         this.pnode.removeClass("disabled");
10863     },
10864
10865     /**
10866      * Sets the content for this TabPanelItem.
10867      * @param {String} content The content
10868      * @param {Boolean} loadScripts true to look for and load scripts
10869      */
10870     setContent : function(content, loadScripts){
10871         this.bodyEl.update(content, loadScripts);
10872     },
10873
10874     /**
10875      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10876      * @return {Roo.UpdateManager} The UpdateManager
10877      */
10878     getUpdateManager : function(){
10879         return this.bodyEl.getUpdateManager();
10880     },
10881
10882     /**
10883      * Set a URL to be used to load the content for this TabPanelItem.
10884      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10885      * @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)
10886      * @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)
10887      * @return {Roo.UpdateManager} The UpdateManager
10888      */
10889     setUrl : function(url, params, loadOnce){
10890         if(this.refreshDelegate){
10891             this.un('activate', this.refreshDelegate);
10892         }
10893         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10894         this.on("activate", this.refreshDelegate);
10895         return this.bodyEl.getUpdateManager();
10896     },
10897
10898     /** @private */
10899     _handleRefresh : function(url, params, loadOnce){
10900         if(!loadOnce || !this.loaded){
10901             var updater = this.bodyEl.getUpdateManager();
10902             updater.update(url, params, this._setLoaded.createDelegate(this));
10903         }
10904     },
10905
10906     /**
10907      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10908      *   Will fail silently if the setUrl method has not been called.
10909      *   This does not activate the panel, just updates its content.
10910      */
10911     refresh : function(){
10912         if(this.refreshDelegate){
10913            this.loaded = false;
10914            this.refreshDelegate();
10915         }
10916     },
10917
10918     /** @private */
10919     _setLoaded : function(){
10920         this.loaded = true;
10921     },
10922
10923     /** @private */
10924     closeClick : function(e){
10925         var o = {};
10926         e.stopEvent();
10927         this.fireEvent("beforeclose", this, o);
10928         if(o.cancel !== true){
10929             this.tabPanel.removeTab(this.id);
10930         }
10931     },
10932     /**
10933      * The text displayed in the tooltip for the close icon.
10934      * @type String
10935      */
10936     closeText : "Close this tab"
10937 });
10938
10939 /** @private */
10940 Roo.TabPanel.prototype.createStrip = function(container){
10941     var strip = document.createElement("div");
10942     strip.className = "x-tabs-wrap";
10943     container.appendChild(strip);
10944     return strip;
10945 };
10946 /** @private */
10947 Roo.TabPanel.prototype.createStripList = function(strip){
10948     // div wrapper for retard IE
10949     // returns the "tr" element.
10950     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10951         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10952         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10953     return strip.firstChild.firstChild.firstChild.firstChild;
10954 };
10955 /** @private */
10956 Roo.TabPanel.prototype.createBody = function(container){
10957     var body = document.createElement("div");
10958     Roo.id(body, "tab-body");
10959     Roo.fly(body).addClass("x-tabs-body");
10960     container.appendChild(body);
10961     return body;
10962 };
10963 /** @private */
10964 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10965     var body = Roo.getDom(id);
10966     if(!body){
10967         body = document.createElement("div");
10968         body.id = id;
10969     }
10970     Roo.fly(body).addClass("x-tabs-item-body");
10971     bodyEl.insertBefore(body, bodyEl.firstChild);
10972     return body;
10973 };
10974 /** @private */
10975 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10976     var td = document.createElement("td");
10977     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10978     //stripEl.appendChild(td);
10979     if(closable){
10980         td.className = "x-tabs-closable";
10981         if(!this.closeTpl){
10982             this.closeTpl = new Roo.Template(
10983                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10984                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10985                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10986             );
10987         }
10988         var el = this.closeTpl.overwrite(td, {"text": text});
10989         var close = el.getElementsByTagName("div")[0];
10990         var inner = el.getElementsByTagName("em")[0];
10991         return {"el": el, "close": close, "inner": inner};
10992     } else {
10993         if(!this.tabTpl){
10994             this.tabTpl = new Roo.Template(
10995                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10996                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
10997             );
10998         }
10999         var el = this.tabTpl.overwrite(td, {"text": text});
11000         var inner = el.getElementsByTagName("em")[0];
11001         return {"el": el, "inner": inner};
11002     }
11003 };/*
11004  * Based on:
11005  * Ext JS Library 1.1.1
11006  * Copyright(c) 2006-2007, Ext JS, LLC.
11007  *
11008  * Originally Released Under LGPL - original licence link has changed is not relivant.
11009  *
11010  * Fork - LGPL
11011  * <script type="text/javascript">
11012  */
11013
11014 /**
11015  * @class Roo.Button
11016  * @extends Roo.util.Observable
11017  * Simple Button class
11018  * @cfg {String} text The button text
11019  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11020  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11021  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11022  * @cfg {Object} scope The scope of the handler
11023  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11024  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11025  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11026  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11027  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11028  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11029    applies if enableToggle = true)
11030  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11031  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11032   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11033  * @constructor
11034  * Create a new button
11035  * @param {Object} config The config object
11036  */
11037 Roo.Button = function(renderTo, config)
11038 {
11039     if (!config) {
11040         config = renderTo;
11041         renderTo = config.renderTo || false;
11042     }
11043     
11044     Roo.apply(this, config);
11045     this.addEvents({
11046         /**
11047              * @event click
11048              * Fires when this button is clicked
11049              * @param {Button} this
11050              * @param {EventObject} e The click event
11051              */
11052             "click" : true,
11053         /**
11054              * @event toggle
11055              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11056              * @param {Button} this
11057              * @param {Boolean} pressed
11058              */
11059             "toggle" : true,
11060         /**
11061              * @event mouseover
11062              * Fires when the mouse hovers over the button
11063              * @param {Button} this
11064              * @param {Event} e The event object
11065              */
11066         'mouseover' : true,
11067         /**
11068              * @event mouseout
11069              * Fires when the mouse exits the button
11070              * @param {Button} this
11071              * @param {Event} e The event object
11072              */
11073         'mouseout': true,
11074          /**
11075              * @event render
11076              * Fires when the button is rendered
11077              * @param {Button} this
11078              */
11079         'render': true
11080     });
11081     if(this.menu){
11082         this.menu = Roo.menu.MenuMgr.get(this.menu);
11083     }
11084     // register listeners first!!  - so render can be captured..
11085     Roo.util.Observable.call(this);
11086     if(renderTo){
11087         this.render(renderTo);
11088     }
11089     
11090   
11091 };
11092
11093 Roo.extend(Roo.Button, Roo.util.Observable, {
11094     /**
11095      * 
11096      */
11097     
11098     /**
11099      * Read-only. True if this button is hidden
11100      * @type Boolean
11101      */
11102     hidden : false,
11103     /**
11104      * Read-only. True if this button is disabled
11105      * @type Boolean
11106      */
11107     disabled : false,
11108     /**
11109      * Read-only. True if this button is pressed (only if enableToggle = true)
11110      * @type Boolean
11111      */
11112     pressed : false,
11113
11114     /**
11115      * @cfg {Number} tabIndex 
11116      * The DOM tabIndex for this button (defaults to undefined)
11117      */
11118     tabIndex : undefined,
11119
11120     /**
11121      * @cfg {Boolean} enableToggle
11122      * True to enable pressed/not pressed toggling (defaults to false)
11123      */
11124     enableToggle: false,
11125     /**
11126      * @cfg {Mixed} menu
11127      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11128      */
11129     menu : undefined,
11130     /**
11131      * @cfg {String} menuAlign
11132      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11133      */
11134     menuAlign : "tl-bl?",
11135
11136     /**
11137      * @cfg {String} iconCls
11138      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11139      */
11140     iconCls : undefined,
11141     /**
11142      * @cfg {String} type
11143      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11144      */
11145     type : 'button',
11146
11147     // private
11148     menuClassTarget: 'tr',
11149
11150     /**
11151      * @cfg {String} clickEvent
11152      * The type of event to map to the button's event handler (defaults to 'click')
11153      */
11154     clickEvent : 'click',
11155
11156     /**
11157      * @cfg {Boolean} handleMouseEvents
11158      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11159      */
11160     handleMouseEvents : true,
11161
11162     /**
11163      * @cfg {String} tooltipType
11164      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11165      */
11166     tooltipType : 'qtip',
11167
11168     /**
11169      * @cfg {String} cls
11170      * A CSS class to apply to the button's main element.
11171      */
11172     
11173     /**
11174      * @cfg {Roo.Template} template (Optional)
11175      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11176      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11177      * require code modifications if required elements (e.g. a button) aren't present.
11178      */
11179
11180     // private
11181     render : function(renderTo){
11182         var btn;
11183         if(this.hideParent){
11184             this.parentEl = Roo.get(renderTo);
11185         }
11186         if(!this.dhconfig){
11187             if(!this.template){
11188                 if(!Roo.Button.buttonTemplate){
11189                     // hideous table template
11190                     Roo.Button.buttonTemplate = new Roo.Template(
11191                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11192                         '<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>',
11193                         "</tr></tbody></table>");
11194                 }
11195                 this.template = Roo.Button.buttonTemplate;
11196             }
11197             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11198             var btnEl = btn.child("button:first");
11199             btnEl.on('focus', this.onFocus, this);
11200             btnEl.on('blur', this.onBlur, this);
11201             if(this.cls){
11202                 btn.addClass(this.cls);
11203             }
11204             if(this.icon){
11205                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11206             }
11207             if(this.iconCls){
11208                 btnEl.addClass(this.iconCls);
11209                 if(!this.cls){
11210                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11211                 }
11212             }
11213             if(this.tabIndex !== undefined){
11214                 btnEl.dom.tabIndex = this.tabIndex;
11215             }
11216             if(this.tooltip){
11217                 if(typeof this.tooltip == 'object'){
11218                     Roo.QuickTips.tips(Roo.apply({
11219                           target: btnEl.id
11220                     }, this.tooltip));
11221                 } else {
11222                     btnEl.dom[this.tooltipType] = this.tooltip;
11223                 }
11224             }
11225         }else{
11226             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11227         }
11228         this.el = btn;
11229         if(this.id){
11230             this.el.dom.id = this.el.id = this.id;
11231         }
11232         if(this.menu){
11233             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11234             this.menu.on("show", this.onMenuShow, this);
11235             this.menu.on("hide", this.onMenuHide, this);
11236         }
11237         btn.addClass("x-btn");
11238         if(Roo.isIE && !Roo.isIE7){
11239             this.autoWidth.defer(1, this);
11240         }else{
11241             this.autoWidth();
11242         }
11243         if(this.handleMouseEvents){
11244             btn.on("mouseover", this.onMouseOver, this);
11245             btn.on("mouseout", this.onMouseOut, this);
11246             btn.on("mousedown", this.onMouseDown, this);
11247         }
11248         btn.on(this.clickEvent, this.onClick, this);
11249         //btn.on("mouseup", this.onMouseUp, this);
11250         if(this.hidden){
11251             this.hide();
11252         }
11253         if(this.disabled){
11254             this.disable();
11255         }
11256         Roo.ButtonToggleMgr.register(this);
11257         if(this.pressed){
11258             this.el.addClass("x-btn-pressed");
11259         }
11260         if(this.repeat){
11261             var repeater = new Roo.util.ClickRepeater(btn,
11262                 typeof this.repeat == "object" ? this.repeat : {}
11263             );
11264             repeater.on("click", this.onClick,  this);
11265         }
11266         
11267         this.fireEvent('render', this);
11268         
11269     },
11270     /**
11271      * Returns the button's underlying element
11272      * @return {Roo.Element} The element
11273      */
11274     getEl : function(){
11275         return this.el;  
11276     },
11277     
11278     /**
11279      * Destroys this Button and removes any listeners.
11280      */
11281     destroy : function(){
11282         Roo.ButtonToggleMgr.unregister(this);
11283         this.el.removeAllListeners();
11284         this.purgeListeners();
11285         this.el.remove();
11286     },
11287
11288     // private
11289     autoWidth : function(){
11290         if(this.el){
11291             this.el.setWidth("auto");
11292             if(Roo.isIE7 && Roo.isStrict){
11293                 var ib = this.el.child('button');
11294                 if(ib && ib.getWidth() > 20){
11295                     ib.clip();
11296                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11297                 }
11298             }
11299             if(this.minWidth){
11300                 if(this.hidden){
11301                     this.el.beginMeasure();
11302                 }
11303                 if(this.el.getWidth() < this.minWidth){
11304                     this.el.setWidth(this.minWidth);
11305                 }
11306                 if(this.hidden){
11307                     this.el.endMeasure();
11308                 }
11309             }
11310         }
11311     },
11312
11313     /**
11314      * Assigns this button's click handler
11315      * @param {Function} handler The function to call when the button is clicked
11316      * @param {Object} scope (optional) Scope for the function passed in
11317      */
11318     setHandler : function(handler, scope){
11319         this.handler = handler;
11320         this.scope = scope;  
11321     },
11322     
11323     /**
11324      * Sets this button's text
11325      * @param {String} text The button text
11326      */
11327     setText : function(text){
11328         this.text = text;
11329         if(this.el){
11330             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11331         }
11332         this.autoWidth();
11333     },
11334     
11335     /**
11336      * Gets the text for this button
11337      * @return {String} The button text
11338      */
11339     getText : function(){
11340         return this.text;  
11341     },
11342     
11343     /**
11344      * Show this button
11345      */
11346     show: function(){
11347         this.hidden = false;
11348         if(this.el){
11349             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11350         }
11351     },
11352     
11353     /**
11354      * Hide this button
11355      */
11356     hide: function(){
11357         this.hidden = true;
11358         if(this.el){
11359             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11360         }
11361     },
11362     
11363     /**
11364      * Convenience function for boolean show/hide
11365      * @param {Boolean} visible True to show, false to hide
11366      */
11367     setVisible: function(visible){
11368         if(visible) {
11369             this.show();
11370         }else{
11371             this.hide();
11372         }
11373     },
11374     
11375     /**
11376      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11377      * @param {Boolean} state (optional) Force a particular state
11378      */
11379     toggle : function(state){
11380         state = state === undefined ? !this.pressed : state;
11381         if(state != this.pressed){
11382             if(state){
11383                 this.el.addClass("x-btn-pressed");
11384                 this.pressed = true;
11385                 this.fireEvent("toggle", this, true);
11386             }else{
11387                 this.el.removeClass("x-btn-pressed");
11388                 this.pressed = false;
11389                 this.fireEvent("toggle", this, false);
11390             }
11391             if(this.toggleHandler){
11392                 this.toggleHandler.call(this.scope || this, this, state);
11393             }
11394         }
11395     },
11396     
11397     /**
11398      * Focus the button
11399      */
11400     focus : function(){
11401         this.el.child('button:first').focus();
11402     },
11403     
11404     /**
11405      * Disable this button
11406      */
11407     disable : function(){
11408         if(this.el){
11409             this.el.addClass("x-btn-disabled");
11410         }
11411         this.disabled = true;
11412     },
11413     
11414     /**
11415      * Enable this button
11416      */
11417     enable : function(){
11418         if(this.el){
11419             this.el.removeClass("x-btn-disabled");
11420         }
11421         this.disabled = false;
11422     },
11423
11424     /**
11425      * Convenience function for boolean enable/disable
11426      * @param {Boolean} enabled True to enable, false to disable
11427      */
11428     setDisabled : function(v){
11429         this[v !== true ? "enable" : "disable"]();
11430     },
11431
11432     // private
11433     onClick : function(e)
11434     {
11435         if(e){
11436             e.preventDefault();
11437         }
11438         if(e.button != 0){
11439             return;
11440         }
11441         if(!this.disabled){
11442             if(this.enableToggle){
11443                 this.toggle();
11444             }
11445             if(this.menu && !this.menu.isVisible()){
11446                 this.menu.show(this.el, this.menuAlign);
11447             }
11448             this.fireEvent("click", this, e);
11449             if(this.handler){
11450                 this.el.removeClass("x-btn-over");
11451                 this.handler.call(this.scope || this, this, e);
11452             }
11453         }
11454     },
11455     // private
11456     onMouseOver : function(e){
11457         if(!this.disabled){
11458             this.el.addClass("x-btn-over");
11459             this.fireEvent('mouseover', this, e);
11460         }
11461     },
11462     // private
11463     onMouseOut : function(e){
11464         if(!e.within(this.el,  true)){
11465             this.el.removeClass("x-btn-over");
11466             this.fireEvent('mouseout', this, e);
11467         }
11468     },
11469     // private
11470     onFocus : function(e){
11471         if(!this.disabled){
11472             this.el.addClass("x-btn-focus");
11473         }
11474     },
11475     // private
11476     onBlur : function(e){
11477         this.el.removeClass("x-btn-focus");
11478     },
11479     // private
11480     onMouseDown : function(e){
11481         if(!this.disabled && e.button == 0){
11482             this.el.addClass("x-btn-click");
11483             Roo.get(document).on('mouseup', this.onMouseUp, this);
11484         }
11485     },
11486     // private
11487     onMouseUp : function(e){
11488         if(e.button == 0){
11489             this.el.removeClass("x-btn-click");
11490             Roo.get(document).un('mouseup', this.onMouseUp, this);
11491         }
11492     },
11493     // private
11494     onMenuShow : function(e){
11495         this.el.addClass("x-btn-menu-active");
11496     },
11497     // private
11498     onMenuHide : function(e){
11499         this.el.removeClass("x-btn-menu-active");
11500     }   
11501 });
11502
11503 // Private utility class used by Button
11504 Roo.ButtonToggleMgr = function(){
11505    var groups = {};
11506    
11507    function toggleGroup(btn, state){
11508        if(state){
11509            var g = groups[btn.toggleGroup];
11510            for(var i = 0, l = g.length; i < l; i++){
11511                if(g[i] != btn){
11512                    g[i].toggle(false);
11513                }
11514            }
11515        }
11516    }
11517    
11518    return {
11519        register : function(btn){
11520            if(!btn.toggleGroup){
11521                return;
11522            }
11523            var g = groups[btn.toggleGroup];
11524            if(!g){
11525                g = groups[btn.toggleGroup] = [];
11526            }
11527            g.push(btn);
11528            btn.on("toggle", toggleGroup);
11529        },
11530        
11531        unregister : function(btn){
11532            if(!btn.toggleGroup){
11533                return;
11534            }
11535            var g = groups[btn.toggleGroup];
11536            if(g){
11537                g.remove(btn);
11538                btn.un("toggle", toggleGroup);
11539            }
11540        }
11541    };
11542 }();/*
11543  * Based on:
11544  * Ext JS Library 1.1.1
11545  * Copyright(c) 2006-2007, Ext JS, LLC.
11546  *
11547  * Originally Released Under LGPL - original licence link has changed is not relivant.
11548  *
11549  * Fork - LGPL
11550  * <script type="text/javascript">
11551  */
11552  
11553 /**
11554  * @class Roo.SplitButton
11555  * @extends Roo.Button
11556  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11557  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11558  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11559  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11560  * @cfg {String} arrowTooltip The title attribute of the arrow
11561  * @constructor
11562  * Create a new menu button
11563  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11564  * @param {Object} config The config object
11565  */
11566 Roo.SplitButton = function(renderTo, config){
11567     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11568     /**
11569      * @event arrowclick
11570      * Fires when this button's arrow is clicked
11571      * @param {SplitButton} this
11572      * @param {EventObject} e The click event
11573      */
11574     this.addEvents({"arrowclick":true});
11575 };
11576
11577 Roo.extend(Roo.SplitButton, Roo.Button, {
11578     render : function(renderTo){
11579         // this is one sweet looking template!
11580         var tpl = new Roo.Template(
11581             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11582             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11583             '<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>',
11584             "</tbody></table></td><td>",
11585             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11586             '<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>',
11587             "</tbody></table></td></tr></table>"
11588         );
11589         var btn = tpl.append(renderTo, [this.text, this.type], true);
11590         var btnEl = btn.child("button");
11591         if(this.cls){
11592             btn.addClass(this.cls);
11593         }
11594         if(this.icon){
11595             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11596         }
11597         if(this.iconCls){
11598             btnEl.addClass(this.iconCls);
11599             if(!this.cls){
11600                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11601             }
11602         }
11603         this.el = btn;
11604         if(this.handleMouseEvents){
11605             btn.on("mouseover", this.onMouseOver, this);
11606             btn.on("mouseout", this.onMouseOut, this);
11607             btn.on("mousedown", this.onMouseDown, this);
11608             btn.on("mouseup", this.onMouseUp, this);
11609         }
11610         btn.on(this.clickEvent, this.onClick, this);
11611         if(this.tooltip){
11612             if(typeof this.tooltip == 'object'){
11613                 Roo.QuickTips.tips(Roo.apply({
11614                       target: btnEl.id
11615                 }, this.tooltip));
11616             } else {
11617                 btnEl.dom[this.tooltipType] = this.tooltip;
11618             }
11619         }
11620         if(this.arrowTooltip){
11621             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11622         }
11623         if(this.hidden){
11624             this.hide();
11625         }
11626         if(this.disabled){
11627             this.disable();
11628         }
11629         if(this.pressed){
11630             this.el.addClass("x-btn-pressed");
11631         }
11632         if(Roo.isIE && !Roo.isIE7){
11633             this.autoWidth.defer(1, this);
11634         }else{
11635             this.autoWidth();
11636         }
11637         if(this.menu){
11638             this.menu.on("show", this.onMenuShow, this);
11639             this.menu.on("hide", this.onMenuHide, this);
11640         }
11641         this.fireEvent('render', this);
11642     },
11643
11644     // private
11645     autoWidth : function(){
11646         if(this.el){
11647             var tbl = this.el.child("table:first");
11648             var tbl2 = this.el.child("table:last");
11649             this.el.setWidth("auto");
11650             tbl.setWidth("auto");
11651             if(Roo.isIE7 && Roo.isStrict){
11652                 var ib = this.el.child('button:first');
11653                 if(ib && ib.getWidth() > 20){
11654                     ib.clip();
11655                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11656                 }
11657             }
11658             if(this.minWidth){
11659                 if(this.hidden){
11660                     this.el.beginMeasure();
11661                 }
11662                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11663                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11664                 }
11665                 if(this.hidden){
11666                     this.el.endMeasure();
11667                 }
11668             }
11669             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11670         } 
11671     },
11672     /**
11673      * Sets this button's click handler
11674      * @param {Function} handler The function to call when the button is clicked
11675      * @param {Object} scope (optional) Scope for the function passed above
11676      */
11677     setHandler : function(handler, scope){
11678         this.handler = handler;
11679         this.scope = scope;  
11680     },
11681     
11682     /**
11683      * Sets this button's arrow click handler
11684      * @param {Function} handler The function to call when the arrow is clicked
11685      * @param {Object} scope (optional) Scope for the function passed above
11686      */
11687     setArrowHandler : function(handler, scope){
11688         this.arrowHandler = handler;
11689         this.scope = scope;  
11690     },
11691     
11692     /**
11693      * Focus the button
11694      */
11695     focus : function(){
11696         if(this.el){
11697             this.el.child("button:first").focus();
11698         }
11699     },
11700
11701     // private
11702     onClick : function(e){
11703         e.preventDefault();
11704         if(!this.disabled){
11705             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11706                 if(this.menu && !this.menu.isVisible()){
11707                     this.menu.show(this.el, this.menuAlign);
11708                 }
11709                 this.fireEvent("arrowclick", this, e);
11710                 if(this.arrowHandler){
11711                     this.arrowHandler.call(this.scope || this, this, e);
11712                 }
11713             }else{
11714                 this.fireEvent("click", this, e);
11715                 if(this.handler){
11716                     this.handler.call(this.scope || this, this, e);
11717                 }
11718             }
11719         }
11720     },
11721     // private
11722     onMouseDown : function(e){
11723         if(!this.disabled){
11724             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11725         }
11726     },
11727     // private
11728     onMouseUp : function(e){
11729         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11730     }   
11731 });
11732
11733
11734 // backwards compat
11735 Roo.MenuButton = Roo.SplitButton;/*
11736  * Based on:
11737  * Ext JS Library 1.1.1
11738  * Copyright(c) 2006-2007, Ext JS, LLC.
11739  *
11740  * Originally Released Under LGPL - original licence link has changed is not relivant.
11741  *
11742  * Fork - LGPL
11743  * <script type="text/javascript">
11744  */
11745
11746 /**
11747  * @class Roo.Toolbar
11748  * Basic Toolbar class.
11749  * @constructor
11750  * Creates a new Toolbar
11751  * @param {Object} container The config object
11752  */ 
11753 Roo.Toolbar = function(container, buttons, config)
11754 {
11755     /// old consturctor format still supported..
11756     if(container instanceof Array){ // omit the container for later rendering
11757         buttons = container;
11758         config = buttons;
11759         container = null;
11760     }
11761     if (typeof(container) == 'object' && container.xtype) {
11762         config = container;
11763         container = config.container;
11764         buttons = config.buttons || []; // not really - use items!!
11765     }
11766     var xitems = [];
11767     if (config && config.items) {
11768         xitems = config.items;
11769         delete config.items;
11770     }
11771     Roo.apply(this, config);
11772     this.buttons = buttons;
11773     
11774     if(container){
11775         this.render(container);
11776     }
11777     this.xitems = xitems;
11778     Roo.each(xitems, function(b) {
11779         this.add(b);
11780     }, this);
11781     
11782 };
11783
11784 Roo.Toolbar.prototype = {
11785     /**
11786      * @cfg {Array} items
11787      * array of button configs or elements to add (will be converted to a MixedCollection)
11788      */
11789     
11790     /**
11791      * @cfg {String/HTMLElement/Element} container
11792      * The id or element that will contain the toolbar
11793      */
11794     // private
11795     render : function(ct){
11796         this.el = Roo.get(ct);
11797         if(this.cls){
11798             this.el.addClass(this.cls);
11799         }
11800         // using a table allows for vertical alignment
11801         // 100% width is needed by Safari...
11802         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11803         this.tr = this.el.child("tr", true);
11804         var autoId = 0;
11805         this.items = new Roo.util.MixedCollection(false, function(o){
11806             return o.id || ("item" + (++autoId));
11807         });
11808         if(this.buttons){
11809             this.add.apply(this, this.buttons);
11810             delete this.buttons;
11811         }
11812     },
11813
11814     /**
11815      * Adds element(s) to the toolbar -- this function takes a variable number of 
11816      * arguments of mixed type and adds them to the toolbar.
11817      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11818      * <ul>
11819      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11820      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11821      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11822      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11823      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11824      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11825      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11826      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11827      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11828      * </ul>
11829      * @param {Mixed} arg2
11830      * @param {Mixed} etc.
11831      */
11832     add : function(){
11833         var a = arguments, l = a.length;
11834         for(var i = 0; i < l; i++){
11835             this._add(a[i]);
11836         }
11837     },
11838     // private..
11839     _add : function(el) {
11840         
11841         if (el.xtype) {
11842             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11843         }
11844         
11845         if (el.applyTo){ // some kind of form field
11846             return this.addField(el);
11847         } 
11848         if (el.render){ // some kind of Toolbar.Item
11849             return this.addItem(el);
11850         }
11851         if (typeof el == "string"){ // string
11852             if(el == "separator" || el == "-"){
11853                 return this.addSeparator();
11854             }
11855             if (el == " "){
11856                 return this.addSpacer();
11857             }
11858             if(el == "->"){
11859                 return this.addFill();
11860             }
11861             return this.addText(el);
11862             
11863         }
11864         if(el.tagName){ // element
11865             return this.addElement(el);
11866         }
11867         if(typeof el == "object"){ // must be button config?
11868             return this.addButton(el);
11869         }
11870         // and now what?!?!
11871         return false;
11872         
11873     },
11874     
11875     /**
11876      * Add an Xtype element
11877      * @param {Object} xtype Xtype Object
11878      * @return {Object} created Object
11879      */
11880     addxtype : function(e){
11881         return this.add(e);  
11882     },
11883     
11884     /**
11885      * Returns the Element for this toolbar.
11886      * @return {Roo.Element}
11887      */
11888     getEl : function(){
11889         return this.el;  
11890     },
11891     
11892     /**
11893      * Adds a separator
11894      * @return {Roo.Toolbar.Item} The separator item
11895      */
11896     addSeparator : function(){
11897         return this.addItem(new Roo.Toolbar.Separator());
11898     },
11899
11900     /**
11901      * Adds a spacer element
11902      * @return {Roo.Toolbar.Spacer} The spacer item
11903      */
11904     addSpacer : function(){
11905         return this.addItem(new Roo.Toolbar.Spacer());
11906     },
11907
11908     /**
11909      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11910      * @return {Roo.Toolbar.Fill} The fill item
11911      */
11912     addFill : function(){
11913         return this.addItem(new Roo.Toolbar.Fill());
11914     },
11915
11916     /**
11917      * Adds any standard HTML element to the toolbar
11918      * @param {String/HTMLElement/Element} el The element or id of the element to add
11919      * @return {Roo.Toolbar.Item} The element's item
11920      */
11921     addElement : function(el){
11922         return this.addItem(new Roo.Toolbar.Item(el));
11923     },
11924     /**
11925      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11926      * @type Roo.util.MixedCollection  
11927      */
11928     items : false,
11929      
11930     /**
11931      * Adds any Toolbar.Item or subclass
11932      * @param {Roo.Toolbar.Item} item
11933      * @return {Roo.Toolbar.Item} The item
11934      */
11935     addItem : function(item){
11936         var td = this.nextBlock();
11937         item.render(td);
11938         this.items.add(item);
11939         return item;
11940     },
11941     
11942     /**
11943      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11944      * @param {Object/Array} config A button config or array of configs
11945      * @return {Roo.Toolbar.Button/Array}
11946      */
11947     addButton : function(config){
11948         if(config instanceof Array){
11949             var buttons = [];
11950             for(var i = 0, len = config.length; i < len; i++) {
11951                 buttons.push(this.addButton(config[i]));
11952             }
11953             return buttons;
11954         }
11955         var b = config;
11956         if(!(config instanceof Roo.Toolbar.Button)){
11957             b = config.split ?
11958                 new Roo.Toolbar.SplitButton(config) :
11959                 new Roo.Toolbar.Button(config);
11960         }
11961         var td = this.nextBlock();
11962         b.render(td);
11963         this.items.add(b);
11964         return b;
11965     },
11966     
11967     /**
11968      * Adds text to the toolbar
11969      * @param {String} text The text to add
11970      * @return {Roo.Toolbar.Item} The element's item
11971      */
11972     addText : function(text){
11973         return this.addItem(new Roo.Toolbar.TextItem(text));
11974     },
11975     
11976     /**
11977      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11978      * @param {Number} index The index where the item is to be inserted
11979      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11980      * @return {Roo.Toolbar.Button/Item}
11981      */
11982     insertButton : function(index, item){
11983         if(item instanceof Array){
11984             var buttons = [];
11985             for(var i = 0, len = item.length; i < len; i++) {
11986                buttons.push(this.insertButton(index + i, item[i]));
11987             }
11988             return buttons;
11989         }
11990         if (!(item instanceof Roo.Toolbar.Button)){
11991            item = new Roo.Toolbar.Button(item);
11992         }
11993         var td = document.createElement("td");
11994         this.tr.insertBefore(td, this.tr.childNodes[index]);
11995         item.render(td);
11996         this.items.insert(index, item);
11997         return item;
11998     },
11999     
12000     /**
12001      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12002      * @param {Object} config
12003      * @return {Roo.Toolbar.Item} The element's item
12004      */
12005     addDom : function(config, returnEl){
12006         var td = this.nextBlock();
12007         Roo.DomHelper.overwrite(td, config);
12008         var ti = new Roo.Toolbar.Item(td.firstChild);
12009         ti.render(td);
12010         this.items.add(ti);
12011         return ti;
12012     },
12013
12014     /**
12015      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12016      * @type Roo.util.MixedCollection  
12017      */
12018     fields : false,
12019     
12020     /**
12021      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12022      * Note: the field should not have been rendered yet. For a field that has already been
12023      * rendered, use {@link #addElement}.
12024      * @param {Roo.form.Field} field
12025      * @return {Roo.ToolbarItem}
12026      */
12027      
12028       
12029     addField : function(field) {
12030         if (!this.fields) {
12031             var autoId = 0;
12032             this.fields = new Roo.util.MixedCollection(false, function(o){
12033                 return o.id || ("item" + (++autoId));
12034             });
12035
12036         }
12037         
12038         var td = this.nextBlock();
12039         field.render(td);
12040         var ti = new Roo.Toolbar.Item(td.firstChild);
12041         ti.render(td);
12042         this.items.add(ti);
12043         this.fields.add(field);
12044         return ti;
12045     },
12046     /**
12047      * Hide the toolbar
12048      * @method hide
12049      */
12050      
12051       
12052     hide : function()
12053     {
12054         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12055         this.el.child('div').hide();
12056     },
12057     /**
12058      * Show the toolbar
12059      * @method show
12060      */
12061     show : function()
12062     {
12063         this.el.child('div').show();
12064     },
12065       
12066     // private
12067     nextBlock : function(){
12068         var td = document.createElement("td");
12069         this.tr.appendChild(td);
12070         return td;
12071     },
12072
12073     // private
12074     destroy : function(){
12075         if(this.items){ // rendered?
12076             Roo.destroy.apply(Roo, this.items.items);
12077         }
12078         if(this.fields){ // rendered?
12079             Roo.destroy.apply(Roo, this.fields.items);
12080         }
12081         Roo.Element.uncache(this.el, this.tr);
12082     }
12083 };
12084
12085 /**
12086  * @class Roo.Toolbar.Item
12087  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12088  * @constructor
12089  * Creates a new Item
12090  * @param {HTMLElement} el 
12091  */
12092 Roo.Toolbar.Item = function(el){
12093     var cfg = {};
12094     if (typeof (el.xtype) != 'undefined') {
12095         cfg = el;
12096         el = cfg.el;
12097     }
12098     
12099     this.el = Roo.getDom(el);
12100     this.id = Roo.id(this.el);
12101     this.hidden = false;
12102     
12103     this.addEvents({
12104          /**
12105              * @event render
12106              * Fires when the button is rendered
12107              * @param {Button} this
12108              */
12109         'render': true
12110     });
12111     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12112 };
12113 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12114 //Roo.Toolbar.Item.prototype = {
12115     
12116     /**
12117      * Get this item's HTML Element
12118      * @return {HTMLElement}
12119      */
12120     getEl : function(){
12121        return this.el;  
12122     },
12123
12124     // private
12125     render : function(td){
12126         
12127          this.td = td;
12128         td.appendChild(this.el);
12129         
12130         this.fireEvent('render', this);
12131     },
12132     
12133     /**
12134      * Removes and destroys this item.
12135      */
12136     destroy : function(){
12137         this.td.parentNode.removeChild(this.td);
12138     },
12139     
12140     /**
12141      * Shows this item.
12142      */
12143     show: function(){
12144         this.hidden = false;
12145         this.td.style.display = "";
12146     },
12147     
12148     /**
12149      * Hides this item.
12150      */
12151     hide: function(){
12152         this.hidden = true;
12153         this.td.style.display = "none";
12154     },
12155     
12156     /**
12157      * Convenience function for boolean show/hide.
12158      * @param {Boolean} visible true to show/false to hide
12159      */
12160     setVisible: function(visible){
12161         if(visible) {
12162             this.show();
12163         }else{
12164             this.hide();
12165         }
12166     },
12167     
12168     /**
12169      * Try to focus this item.
12170      */
12171     focus : function(){
12172         Roo.fly(this.el).focus();
12173     },
12174     
12175     /**
12176      * Disables this item.
12177      */
12178     disable : function(){
12179         Roo.fly(this.td).addClass("x-item-disabled");
12180         this.disabled = true;
12181         this.el.disabled = true;
12182     },
12183     
12184     /**
12185      * Enables this item.
12186      */
12187     enable : function(){
12188         Roo.fly(this.td).removeClass("x-item-disabled");
12189         this.disabled = false;
12190         this.el.disabled = false;
12191     }
12192 });
12193
12194
12195 /**
12196  * @class Roo.Toolbar.Separator
12197  * @extends Roo.Toolbar.Item
12198  * A simple toolbar separator class
12199  * @constructor
12200  * Creates a new Separator
12201  */
12202 Roo.Toolbar.Separator = function(cfg){
12203     
12204     var s = document.createElement("span");
12205     s.className = "ytb-sep";
12206     if (cfg) {
12207         cfg.el = s;
12208     }
12209     
12210     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12211 };
12212 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12213     enable:Roo.emptyFn,
12214     disable:Roo.emptyFn,
12215     focus:Roo.emptyFn
12216 });
12217
12218 /**
12219  * @class Roo.Toolbar.Spacer
12220  * @extends Roo.Toolbar.Item
12221  * A simple element that adds extra horizontal space to a toolbar.
12222  * @constructor
12223  * Creates a new Spacer
12224  */
12225 Roo.Toolbar.Spacer = function(cfg){
12226     var s = document.createElement("div");
12227     s.className = "ytb-spacer";
12228     if (cfg) {
12229         cfg.el = s;
12230     }
12231     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12232 };
12233 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12234     enable:Roo.emptyFn,
12235     disable:Roo.emptyFn,
12236     focus:Roo.emptyFn
12237 });
12238
12239 /**
12240  * @class Roo.Toolbar.Fill
12241  * @extends Roo.Toolbar.Spacer
12242  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12243  * @constructor
12244  * Creates a new Spacer
12245  */
12246 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12247     // private
12248     render : function(td){
12249         td.style.width = '100%';
12250         Roo.Toolbar.Fill.superclass.render.call(this, td);
12251     }
12252 });
12253
12254 /**
12255  * @class Roo.Toolbar.TextItem
12256  * @extends Roo.Toolbar.Item
12257  * A simple class that renders text directly into a toolbar.
12258  * @constructor
12259  * Creates a new TextItem
12260  * @param {String} text
12261  */
12262 Roo.Toolbar.TextItem = function(cfg){
12263     var  text = cfg || "";
12264     if (typeof(cfg) == 'object') {
12265         text = cfg.text || "";
12266     }  else {
12267         cfg = null;
12268     }
12269     var s = document.createElement("span");
12270     s.className = "ytb-text";
12271     s.innerHTML = text;
12272     if (cfg) {
12273         cfg.el  = s;
12274     }
12275     
12276     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12277 };
12278 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12279     
12280      
12281     enable:Roo.emptyFn,
12282     disable:Roo.emptyFn,
12283     focus:Roo.emptyFn
12284 });
12285
12286 /**
12287  * @class Roo.Toolbar.Button
12288  * @extends Roo.Button
12289  * A button that renders into a toolbar.
12290  * @constructor
12291  * Creates a new Button
12292  * @param {Object} config A standard {@link Roo.Button} config object
12293  */
12294 Roo.Toolbar.Button = function(config){
12295     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12296 };
12297 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12298     render : function(td){
12299         this.td = td;
12300         Roo.Toolbar.Button.superclass.render.call(this, td);
12301     },
12302     
12303     /**
12304      * Removes and destroys this button
12305      */
12306     destroy : function(){
12307         Roo.Toolbar.Button.superclass.destroy.call(this);
12308         this.td.parentNode.removeChild(this.td);
12309     },
12310     
12311     /**
12312      * Shows this button
12313      */
12314     show: function(){
12315         this.hidden = false;
12316         this.td.style.display = "";
12317     },
12318     
12319     /**
12320      * Hides this button
12321      */
12322     hide: function(){
12323         this.hidden = true;
12324         this.td.style.display = "none";
12325     },
12326
12327     /**
12328      * Disables this item
12329      */
12330     disable : function(){
12331         Roo.fly(this.td).addClass("x-item-disabled");
12332         this.disabled = true;
12333     },
12334
12335     /**
12336      * Enables this item
12337      */
12338     enable : function(){
12339         Roo.fly(this.td).removeClass("x-item-disabled");
12340         this.disabled = false;
12341     }
12342 });
12343 // backwards compat
12344 Roo.ToolbarButton = Roo.Toolbar.Button;
12345
12346 /**
12347  * @class Roo.Toolbar.SplitButton
12348  * @extends Roo.SplitButton
12349  * A menu button that renders into a toolbar.
12350  * @constructor
12351  * Creates a new SplitButton
12352  * @param {Object} config A standard {@link Roo.SplitButton} config object
12353  */
12354 Roo.Toolbar.SplitButton = function(config){
12355     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12356 };
12357 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12358     render : function(td){
12359         this.td = td;
12360         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12361     },
12362     
12363     /**
12364      * Removes and destroys this button
12365      */
12366     destroy : function(){
12367         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12368         this.td.parentNode.removeChild(this.td);
12369     },
12370     
12371     /**
12372      * Shows this button
12373      */
12374     show: function(){
12375         this.hidden = false;
12376         this.td.style.display = "";
12377     },
12378     
12379     /**
12380      * Hides this button
12381      */
12382     hide: function(){
12383         this.hidden = true;
12384         this.td.style.display = "none";
12385     }
12386 });
12387
12388 // backwards compat
12389 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12390  * Based on:
12391  * Ext JS Library 1.1.1
12392  * Copyright(c) 2006-2007, Ext JS, LLC.
12393  *
12394  * Originally Released Under LGPL - original licence link has changed is not relivant.
12395  *
12396  * Fork - LGPL
12397  * <script type="text/javascript">
12398  */
12399  
12400 /**
12401  * @class Roo.PagingToolbar
12402  * @extends Roo.Toolbar
12403  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12404  * @constructor
12405  * Create a new PagingToolbar
12406  * @param {Object} config The config object
12407  */
12408 Roo.PagingToolbar = function(el, ds, config)
12409 {
12410     // old args format still supported... - xtype is prefered..
12411     if (typeof(el) == 'object' && el.xtype) {
12412         // created from xtype...
12413         config = el;
12414         ds = el.dataSource;
12415         el = config.container;
12416     }
12417     var items = [];
12418     if (config.items) {
12419         items = config.items;
12420         config.items = [];
12421     }
12422     
12423     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12424     this.ds = ds;
12425     this.cursor = 0;
12426     this.renderButtons(this.el);
12427     this.bind(ds);
12428     
12429     // supprot items array.
12430    
12431     Roo.each(items, function(e) {
12432         this.add(Roo.factory(e));
12433     },this);
12434     
12435 };
12436
12437 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12438     /**
12439      * @cfg {Roo.data.Store} dataSource
12440      * The underlying data store providing the paged data
12441      */
12442     /**
12443      * @cfg {String/HTMLElement/Element} container
12444      * container The id or element that will contain the toolbar
12445      */
12446     /**
12447      * @cfg {Boolean} displayInfo
12448      * True to display the displayMsg (defaults to false)
12449      */
12450     /**
12451      * @cfg {Number} pageSize
12452      * The number of records to display per page (defaults to 20)
12453      */
12454     pageSize: 20,
12455     /**
12456      * @cfg {String} displayMsg
12457      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12458      */
12459     displayMsg : 'Displaying {0} - {1} of {2}',
12460     /**
12461      * @cfg {String} emptyMsg
12462      * The message to display when no records are found (defaults to "No data to display")
12463      */
12464     emptyMsg : 'No data to display',
12465     /**
12466      * Customizable piece of the default paging text (defaults to "Page")
12467      * @type String
12468      */
12469     beforePageText : "Page",
12470     /**
12471      * Customizable piece of the default paging text (defaults to "of %0")
12472      * @type String
12473      */
12474     afterPageText : "of {0}",
12475     /**
12476      * Customizable piece of the default paging text (defaults to "First Page")
12477      * @type String
12478      */
12479     firstText : "First Page",
12480     /**
12481      * Customizable piece of the default paging text (defaults to "Previous Page")
12482      * @type String
12483      */
12484     prevText : "Previous Page",
12485     /**
12486      * Customizable piece of the default paging text (defaults to "Next Page")
12487      * @type String
12488      */
12489     nextText : "Next Page",
12490     /**
12491      * Customizable piece of the default paging text (defaults to "Last Page")
12492      * @type String
12493      */
12494     lastText : "Last Page",
12495     /**
12496      * Customizable piece of the default paging text (defaults to "Refresh")
12497      * @type String
12498      */
12499     refreshText : "Refresh",
12500
12501     // private
12502     renderButtons : function(el){
12503         Roo.PagingToolbar.superclass.render.call(this, el);
12504         this.first = this.addButton({
12505             tooltip: this.firstText,
12506             cls: "x-btn-icon x-grid-page-first",
12507             disabled: true,
12508             handler: this.onClick.createDelegate(this, ["first"])
12509         });
12510         this.prev = this.addButton({
12511             tooltip: this.prevText,
12512             cls: "x-btn-icon x-grid-page-prev",
12513             disabled: true,
12514             handler: this.onClick.createDelegate(this, ["prev"])
12515         });
12516         //this.addSeparator();
12517         this.add(this.beforePageText);
12518         this.field = Roo.get(this.addDom({
12519            tag: "input",
12520            type: "text",
12521            size: "3",
12522            value: "1",
12523            cls: "x-grid-page-number"
12524         }).el);
12525         this.field.on("keydown", this.onPagingKeydown, this);
12526         this.field.on("focus", function(){this.dom.select();});
12527         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12528         this.field.setHeight(18);
12529         //this.addSeparator();
12530         this.next = this.addButton({
12531             tooltip: this.nextText,
12532             cls: "x-btn-icon x-grid-page-next",
12533             disabled: true,
12534             handler: this.onClick.createDelegate(this, ["next"])
12535         });
12536         this.last = this.addButton({
12537             tooltip: this.lastText,
12538             cls: "x-btn-icon x-grid-page-last",
12539             disabled: true,
12540             handler: this.onClick.createDelegate(this, ["last"])
12541         });
12542         //this.addSeparator();
12543         this.loading = this.addButton({
12544             tooltip: this.refreshText,
12545             cls: "x-btn-icon x-grid-loading",
12546             handler: this.onClick.createDelegate(this, ["refresh"])
12547         });
12548
12549         if(this.displayInfo){
12550             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12551         }
12552     },
12553
12554     // private
12555     updateInfo : function(){
12556         if(this.displayEl){
12557             var count = this.ds.getCount();
12558             var msg = count == 0 ?
12559                 this.emptyMsg :
12560                 String.format(
12561                     this.displayMsg,
12562                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12563                 );
12564             this.displayEl.update(msg);
12565         }
12566     },
12567
12568     // private
12569     onLoad : function(ds, r, o){
12570        this.cursor = o.params ? o.params.start : 0;
12571        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12572
12573        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12574        this.field.dom.value = ap;
12575        this.first.setDisabled(ap == 1);
12576        this.prev.setDisabled(ap == 1);
12577        this.next.setDisabled(ap == ps);
12578        this.last.setDisabled(ap == ps);
12579        this.loading.enable();
12580        this.updateInfo();
12581     },
12582
12583     // private
12584     getPageData : function(){
12585         var total = this.ds.getTotalCount();
12586         return {
12587             total : total,
12588             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12589             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12590         };
12591     },
12592
12593     // private
12594     onLoadError : function(){
12595         this.loading.enable();
12596     },
12597
12598     // private
12599     onPagingKeydown : function(e){
12600         var k = e.getKey();
12601         var d = this.getPageData();
12602         if(k == e.RETURN){
12603             var v = this.field.dom.value, pageNum;
12604             if(!v || isNaN(pageNum = parseInt(v, 10))){
12605                 this.field.dom.value = d.activePage;
12606                 return;
12607             }
12608             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12609             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12610             e.stopEvent();
12611         }
12612         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))
12613         {
12614           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12615           this.field.dom.value = pageNum;
12616           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12617           e.stopEvent();
12618         }
12619         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12620         {
12621           var v = this.field.dom.value, pageNum; 
12622           var increment = (e.shiftKey) ? 10 : 1;
12623           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12624             increment *= -1;
12625           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12626             this.field.dom.value = d.activePage;
12627             return;
12628           }
12629           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12630           {
12631             this.field.dom.value = parseInt(v, 10) + increment;
12632             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12633             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12634           }
12635           e.stopEvent();
12636         }
12637     },
12638
12639     // private
12640     beforeLoad : function(){
12641         if(this.loading){
12642             this.loading.disable();
12643         }
12644     },
12645
12646     // private
12647     onClick : function(which){
12648         var ds = this.ds;
12649         switch(which){
12650             case "first":
12651                 ds.load({params:{start: 0, limit: this.pageSize}});
12652             break;
12653             case "prev":
12654                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12655             break;
12656             case "next":
12657                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12658             break;
12659             case "last":
12660                 var total = ds.getTotalCount();
12661                 var extra = total % this.pageSize;
12662                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12663                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12664             break;
12665             case "refresh":
12666                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12667             break;
12668         }
12669     },
12670
12671     /**
12672      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12673      * @param {Roo.data.Store} store The data store to unbind
12674      */
12675     unbind : function(ds){
12676         ds.un("beforeload", this.beforeLoad, this);
12677         ds.un("load", this.onLoad, this);
12678         ds.un("loadexception", this.onLoadError, this);
12679         ds.un("remove", this.updateInfo, this);
12680         ds.un("add", this.updateInfo, this);
12681         this.ds = undefined;
12682     },
12683
12684     /**
12685      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12686      * @param {Roo.data.Store} store The data store to bind
12687      */
12688     bind : function(ds){
12689         ds.on("beforeload", this.beforeLoad, this);
12690         ds.on("load", this.onLoad, this);
12691         ds.on("loadexception", this.onLoadError, this);
12692         ds.on("remove", this.updateInfo, this);
12693         ds.on("add", this.updateInfo, this);
12694         this.ds = ds;
12695     }
12696 });/*
12697  * Based on:
12698  * Ext JS Library 1.1.1
12699  * Copyright(c) 2006-2007, Ext JS, LLC.
12700  *
12701  * Originally Released Under LGPL - original licence link has changed is not relivant.
12702  *
12703  * Fork - LGPL
12704  * <script type="text/javascript">
12705  */
12706
12707 /**
12708  * @class Roo.Resizable
12709  * @extends Roo.util.Observable
12710  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12711  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12712  * 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
12713  * the element will be wrapped for you automatically.</p>
12714  * <p>Here is the list of valid resize handles:</p>
12715  * <pre>
12716 Value   Description
12717 ------  -------------------
12718  'n'     north
12719  's'     south
12720  'e'     east
12721  'w'     west
12722  'nw'    northwest
12723  'sw'    southwest
12724  'se'    southeast
12725  'ne'    northeast
12726  'hd'    horizontal drag
12727  'all'   all
12728 </pre>
12729  * <p>Here's an example showing the creation of a typical Resizable:</p>
12730  * <pre><code>
12731 var resizer = new Roo.Resizable("element-id", {
12732     handles: 'all',
12733     minWidth: 200,
12734     minHeight: 100,
12735     maxWidth: 500,
12736     maxHeight: 400,
12737     pinned: true
12738 });
12739 resizer.on("resize", myHandler);
12740 </code></pre>
12741  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12742  * resizer.east.setDisplayed(false);</p>
12743  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12744  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12745  * resize operation's new size (defaults to [0, 0])
12746  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12747  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12748  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12749  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12750  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12751  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12752  * @cfg {Number} width The width of the element in pixels (defaults to null)
12753  * @cfg {Number} height The height of the element in pixels (defaults to null)
12754  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12755  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12756  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12757  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12758  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12759  * in favor of the handles config option (defaults to false)
12760  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12761  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12762  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12763  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12764  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12765  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12766  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12767  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12768  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12769  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12770  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12771  * @constructor
12772  * Create a new resizable component
12773  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12774  * @param {Object} config configuration options
12775   */
12776 Roo.Resizable = function(el, config)
12777 {
12778     this.el = Roo.get(el);
12779
12780     if(config && config.wrap){
12781         config.resizeChild = this.el;
12782         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12783         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12784         this.el.setStyle("overflow", "hidden");
12785         this.el.setPositioning(config.resizeChild.getPositioning());
12786         config.resizeChild.clearPositioning();
12787         if(!config.width || !config.height){
12788             var csize = config.resizeChild.getSize();
12789             this.el.setSize(csize.width, csize.height);
12790         }
12791         if(config.pinned && !config.adjustments){
12792             config.adjustments = "auto";
12793         }
12794     }
12795
12796     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12797     this.proxy.unselectable();
12798     this.proxy.enableDisplayMode('block');
12799
12800     Roo.apply(this, config);
12801
12802     if(this.pinned){
12803         this.disableTrackOver = true;
12804         this.el.addClass("x-resizable-pinned");
12805     }
12806     // if the element isn't positioned, make it relative
12807     var position = this.el.getStyle("position");
12808     if(position != "absolute" && position != "fixed"){
12809         this.el.setStyle("position", "relative");
12810     }
12811     if(!this.handles){ // no handles passed, must be legacy style
12812         this.handles = 's,e,se';
12813         if(this.multiDirectional){
12814             this.handles += ',n,w';
12815         }
12816     }
12817     if(this.handles == "all"){
12818         this.handles = "n s e w ne nw se sw";
12819     }
12820     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12821     var ps = Roo.Resizable.positions;
12822     for(var i = 0, len = hs.length; i < len; i++){
12823         if(hs[i] && ps[hs[i]]){
12824             var pos = ps[hs[i]];
12825             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12826         }
12827     }
12828     // legacy
12829     this.corner = this.southeast;
12830     
12831     // updateBox = the box can move..
12832     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12833         this.updateBox = true;
12834     }
12835
12836     this.activeHandle = null;
12837
12838     if(this.resizeChild){
12839         if(typeof this.resizeChild == "boolean"){
12840             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12841         }else{
12842             this.resizeChild = Roo.get(this.resizeChild, true);
12843         }
12844     }
12845     
12846     if(this.adjustments == "auto"){
12847         var rc = this.resizeChild;
12848         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12849         if(rc && (hw || hn)){
12850             rc.position("relative");
12851             rc.setLeft(hw ? hw.el.getWidth() : 0);
12852             rc.setTop(hn ? hn.el.getHeight() : 0);
12853         }
12854         this.adjustments = [
12855             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12856             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12857         ];
12858     }
12859
12860     if(this.draggable){
12861         this.dd = this.dynamic ?
12862             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12863         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12864     }
12865
12866     // public events
12867     this.addEvents({
12868         /**
12869          * @event beforeresize
12870          * Fired before resize is allowed. Set enabled to false to cancel resize.
12871          * @param {Roo.Resizable} this
12872          * @param {Roo.EventObject} e The mousedown event
12873          */
12874         "beforeresize" : true,
12875         /**
12876          * @event resizing
12877          * Fired a resizing.
12878          * @param {Roo.Resizable} this
12879          * @param {Number} x The new x position
12880          * @param {Number} y The new y position
12881          * @param {Number} w The new w width
12882          * @param {Number} h The new h hight
12883          * @param {Roo.EventObject} e The mouseup event
12884          */
12885         "resizing" : true,
12886         /**
12887          * @event resize
12888          * Fired after a resize.
12889          * @param {Roo.Resizable} this
12890          * @param {Number} width The new width
12891          * @param {Number} height The new height
12892          * @param {Roo.EventObject} e The mouseup event
12893          */
12894         "resize" : true
12895     });
12896
12897     if(this.width !== null && this.height !== null){
12898         this.resizeTo(this.width, this.height);
12899     }else{
12900         this.updateChildSize();
12901     }
12902     if(Roo.isIE){
12903         this.el.dom.style.zoom = 1;
12904     }
12905     Roo.Resizable.superclass.constructor.call(this);
12906 };
12907
12908 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12909         resizeChild : false,
12910         adjustments : [0, 0],
12911         minWidth : 5,
12912         minHeight : 5,
12913         maxWidth : 10000,
12914         maxHeight : 10000,
12915         enabled : true,
12916         animate : false,
12917         duration : .35,
12918         dynamic : false,
12919         handles : false,
12920         multiDirectional : false,
12921         disableTrackOver : false,
12922         easing : 'easeOutStrong',
12923         widthIncrement : 0,
12924         heightIncrement : 0,
12925         pinned : false,
12926         width : null,
12927         height : null,
12928         preserveRatio : false,
12929         transparent: false,
12930         minX: 0,
12931         minY: 0,
12932         draggable: false,
12933
12934         /**
12935          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12936          */
12937         constrainTo: undefined,
12938         /**
12939          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12940          */
12941         resizeRegion: undefined,
12942
12943
12944     /**
12945      * Perform a manual resize
12946      * @param {Number} width
12947      * @param {Number} height
12948      */
12949     resizeTo : function(width, height){
12950         this.el.setSize(width, height);
12951         this.updateChildSize();
12952         this.fireEvent("resize", this, width, height, null);
12953     },
12954
12955     // private
12956     startSizing : function(e, handle){
12957         this.fireEvent("beforeresize", this, e);
12958         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12959
12960             if(!this.overlay){
12961                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12962                 this.overlay.unselectable();
12963                 this.overlay.enableDisplayMode("block");
12964                 this.overlay.on("mousemove", this.onMouseMove, this);
12965                 this.overlay.on("mouseup", this.onMouseUp, this);
12966             }
12967             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12968
12969             this.resizing = true;
12970             this.startBox = this.el.getBox();
12971             this.startPoint = e.getXY();
12972             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12973                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12974
12975             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12976             this.overlay.show();
12977
12978             if(this.constrainTo) {
12979                 var ct = Roo.get(this.constrainTo);
12980                 this.resizeRegion = ct.getRegion().adjust(
12981                     ct.getFrameWidth('t'),
12982                     ct.getFrameWidth('l'),
12983                     -ct.getFrameWidth('b'),
12984                     -ct.getFrameWidth('r')
12985                 );
12986             }
12987
12988             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12989             this.proxy.show();
12990             this.proxy.setBox(this.startBox);
12991             if(!this.dynamic){
12992                 this.proxy.setStyle('visibility', 'visible');
12993             }
12994         }
12995     },
12996
12997     // private
12998     onMouseDown : function(handle, e){
12999         if(this.enabled){
13000             e.stopEvent();
13001             this.activeHandle = handle;
13002             this.startSizing(e, handle);
13003         }
13004     },
13005
13006     // private
13007     onMouseUp : function(e){
13008         var size = this.resizeElement();
13009         this.resizing = false;
13010         this.handleOut();
13011         this.overlay.hide();
13012         this.proxy.hide();
13013         this.fireEvent("resize", this, size.width, size.height, e);
13014     },
13015
13016     // private
13017     updateChildSize : function(){
13018         
13019         if(this.resizeChild){
13020             var el = this.el;
13021             var child = this.resizeChild;
13022             var adj = this.adjustments;
13023             if(el.dom.offsetWidth){
13024                 var b = el.getSize(true);
13025                 child.setSize(b.width+adj[0], b.height+adj[1]);
13026             }
13027             // Second call here for IE
13028             // The first call enables instant resizing and
13029             // the second call corrects scroll bars if they
13030             // exist
13031             if(Roo.isIE){
13032                 setTimeout(function(){
13033                     if(el.dom.offsetWidth){
13034                         var b = el.getSize(true);
13035                         child.setSize(b.width+adj[0], b.height+adj[1]);
13036                     }
13037                 }, 10);
13038             }
13039         }
13040     },
13041
13042     // private
13043     snap : function(value, inc, min){
13044         if(!inc || !value) return value;
13045         var newValue = value;
13046         var m = value % inc;
13047         if(m > 0){
13048             if(m > (inc/2)){
13049                 newValue = value + (inc-m);
13050             }else{
13051                 newValue = value - m;
13052             }
13053         }
13054         return Math.max(min, newValue);
13055     },
13056
13057     // private
13058     resizeElement : function(){
13059         var box = this.proxy.getBox();
13060         if(this.updateBox){
13061             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13062         }else{
13063             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13064         }
13065         this.updateChildSize();
13066         if(!this.dynamic){
13067             this.proxy.hide();
13068         }
13069         return box;
13070     },
13071
13072     // private
13073     constrain : function(v, diff, m, mx){
13074         if(v - diff < m){
13075             diff = v - m;
13076         }else if(v - diff > mx){
13077             diff = mx - v;
13078         }
13079         return diff;
13080     },
13081
13082     // private
13083     onMouseMove : function(e){
13084         
13085         if(this.enabled){
13086             try{// try catch so if something goes wrong the user doesn't get hung
13087
13088             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13089                 return;
13090             }
13091
13092             //var curXY = this.startPoint;
13093             var curSize = this.curSize || this.startBox;
13094             var x = this.startBox.x, y = this.startBox.y;
13095             var ox = x, oy = y;
13096             var w = curSize.width, h = curSize.height;
13097             var ow = w, oh = h;
13098             var mw = this.minWidth, mh = this.minHeight;
13099             var mxw = this.maxWidth, mxh = this.maxHeight;
13100             var wi = this.widthIncrement;
13101             var hi = this.heightIncrement;
13102
13103             var eventXY = e.getXY();
13104             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13105             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13106
13107             var pos = this.activeHandle.position;
13108
13109             switch(pos){
13110                 case "east":
13111                     w += diffX;
13112                     w = Math.min(Math.max(mw, w), mxw);
13113                     break;
13114              
13115                 case "south":
13116                     h += diffY;
13117                     h = Math.min(Math.max(mh, h), mxh);
13118                     break;
13119                 case "southeast":
13120                     w += diffX;
13121                     h += diffY;
13122                     w = Math.min(Math.max(mw, w), mxw);
13123                     h = Math.min(Math.max(mh, h), mxh);
13124                     break;
13125                 case "north":
13126                     diffY = this.constrain(h, diffY, mh, mxh);
13127                     y += diffY;
13128                     h -= diffY;
13129                     break;
13130                 case "hdrag":
13131                     
13132                     if (wi) {
13133                         var adiffX = Math.abs(diffX);
13134                         var sub = (adiffX % wi); // how much 
13135                         if (sub > (wi/2)) { // far enough to snap
13136                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13137                         } else {
13138                             // remove difference.. 
13139                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13140                         }
13141                     }
13142                     x += diffX;
13143                     x = Math.max(this.minX, x);
13144                     break;
13145                 case "west":
13146                     diffX = this.constrain(w, diffX, mw, mxw);
13147                     x += diffX;
13148                     w -= diffX;
13149                     break;
13150                 case "northeast":
13151                     w += diffX;
13152                     w = Math.min(Math.max(mw, w), mxw);
13153                     diffY = this.constrain(h, diffY, mh, mxh);
13154                     y += diffY;
13155                     h -= diffY;
13156                     break;
13157                 case "northwest":
13158                     diffX = this.constrain(w, diffX, mw, mxw);
13159                     diffY = this.constrain(h, diffY, mh, mxh);
13160                     y += diffY;
13161                     h -= diffY;
13162                     x += diffX;
13163                     w -= diffX;
13164                     break;
13165                case "southwest":
13166                     diffX = this.constrain(w, diffX, mw, mxw);
13167                     h += diffY;
13168                     h = Math.min(Math.max(mh, h), mxh);
13169                     x += diffX;
13170                     w -= diffX;
13171                     break;
13172             }
13173
13174             var sw = this.snap(w, wi, mw);
13175             var sh = this.snap(h, hi, mh);
13176             if(sw != w || sh != h){
13177                 switch(pos){
13178                     case "northeast":
13179                         y -= sh - h;
13180                     break;
13181                     case "north":
13182                         y -= sh - h;
13183                         break;
13184                     case "southwest":
13185                         x -= sw - w;
13186                     break;
13187                     case "west":
13188                         x -= sw - w;
13189                         break;
13190                     case "northwest":
13191                         x -= sw - w;
13192                         y -= sh - h;
13193                     break;
13194                 }
13195                 w = sw;
13196                 h = sh;
13197             }
13198
13199             if(this.preserveRatio){
13200                 switch(pos){
13201                     case "southeast":
13202                     case "east":
13203                         h = oh * (w/ow);
13204                         h = Math.min(Math.max(mh, h), mxh);
13205                         w = ow * (h/oh);
13206                        break;
13207                     case "south":
13208                         w = ow * (h/oh);
13209                         w = Math.min(Math.max(mw, w), mxw);
13210                         h = oh * (w/ow);
13211                         break;
13212                     case "northeast":
13213                         w = ow * (h/oh);
13214                         w = Math.min(Math.max(mw, w), mxw);
13215                         h = oh * (w/ow);
13216                     break;
13217                     case "north":
13218                         var tw = w;
13219                         w = ow * (h/oh);
13220                         w = Math.min(Math.max(mw, w), mxw);
13221                         h = oh * (w/ow);
13222                         x += (tw - w) / 2;
13223                         break;
13224                     case "southwest":
13225                         h = oh * (w/ow);
13226                         h = Math.min(Math.max(mh, h), mxh);
13227                         var tw = w;
13228                         w = ow * (h/oh);
13229                         x += tw - w;
13230                         break;
13231                     case "west":
13232                         var th = h;
13233                         h = oh * (w/ow);
13234                         h = Math.min(Math.max(mh, h), mxh);
13235                         y += (th - h) / 2;
13236                         var tw = w;
13237                         w = ow * (h/oh);
13238                         x += tw - w;
13239                        break;
13240                     case "northwest":
13241                         var tw = w;
13242                         var th = h;
13243                         h = oh * (w/ow);
13244                         h = Math.min(Math.max(mh, h), mxh);
13245                         w = ow * (h/oh);
13246                         y += th - h;
13247                         x += tw - w;
13248                        break;
13249
13250                 }
13251             }
13252             if (pos == 'hdrag') {
13253                 w = ow;
13254             }
13255             this.proxy.setBounds(x, y, w, h);
13256             if(this.dynamic){
13257                 this.resizeElement();
13258             }
13259             }catch(e){}
13260         }
13261         this.fireEvent("resizing", this, x, y, w, h, e);
13262     },
13263
13264     // private
13265     handleOver : function(){
13266         if(this.enabled){
13267             this.el.addClass("x-resizable-over");
13268         }
13269     },
13270
13271     // private
13272     handleOut : function(){
13273         if(!this.resizing){
13274             this.el.removeClass("x-resizable-over");
13275         }
13276     },
13277
13278     /**
13279      * Returns the element this component is bound to.
13280      * @return {Roo.Element}
13281      */
13282     getEl : function(){
13283         return this.el;
13284     },
13285
13286     /**
13287      * Returns the resizeChild element (or null).
13288      * @return {Roo.Element}
13289      */
13290     getResizeChild : function(){
13291         return this.resizeChild;
13292     },
13293     groupHandler : function()
13294     {
13295         
13296     },
13297     /**
13298      * Destroys this resizable. If the element was wrapped and
13299      * removeEl is not true then the element remains.
13300      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13301      */
13302     destroy : function(removeEl){
13303         this.proxy.remove();
13304         if(this.overlay){
13305             this.overlay.removeAllListeners();
13306             this.overlay.remove();
13307         }
13308         var ps = Roo.Resizable.positions;
13309         for(var k in ps){
13310             if(typeof ps[k] != "function" && this[ps[k]]){
13311                 var h = this[ps[k]];
13312                 h.el.removeAllListeners();
13313                 h.el.remove();
13314             }
13315         }
13316         if(removeEl){
13317             this.el.update("");
13318             this.el.remove();
13319         }
13320     }
13321 });
13322
13323 // private
13324 // hash to map config positions to true positions
13325 Roo.Resizable.positions = {
13326     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13327     hd: "hdrag"
13328 };
13329
13330 // private
13331 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13332     if(!this.tpl){
13333         // only initialize the template if resizable is used
13334         var tpl = Roo.DomHelper.createTemplate(
13335             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13336         );
13337         tpl.compile();
13338         Roo.Resizable.Handle.prototype.tpl = tpl;
13339     }
13340     this.position = pos;
13341     this.rz = rz;
13342     // show north drag fro topdra
13343     var handlepos = pos == 'hdrag' ? 'north' : pos;
13344     
13345     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13346     if (pos == 'hdrag') {
13347         this.el.setStyle('cursor', 'pointer');
13348     }
13349     this.el.unselectable();
13350     if(transparent){
13351         this.el.setOpacity(0);
13352     }
13353     this.el.on("mousedown", this.onMouseDown, this);
13354     if(!disableTrackOver){
13355         this.el.on("mouseover", this.onMouseOver, this);
13356         this.el.on("mouseout", this.onMouseOut, this);
13357     }
13358 };
13359
13360 // private
13361 Roo.Resizable.Handle.prototype = {
13362     afterResize : function(rz){
13363         Roo.log('after?');
13364         // do nothing
13365     },
13366     // private
13367     onMouseDown : function(e){
13368         this.rz.onMouseDown(this, e);
13369     },
13370     // private
13371     onMouseOver : function(e){
13372         this.rz.handleOver(this, e);
13373     },
13374     // private
13375     onMouseOut : function(e){
13376         this.rz.handleOut(this, e);
13377     }
13378 };/*
13379  * Based on:
13380  * Ext JS Library 1.1.1
13381  * Copyright(c) 2006-2007, Ext JS, LLC.
13382  *
13383  * Originally Released Under LGPL - original licence link has changed is not relivant.
13384  *
13385  * Fork - LGPL
13386  * <script type="text/javascript">
13387  */
13388
13389 /**
13390  * @class Roo.Editor
13391  * @extends Roo.Component
13392  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13393  * @constructor
13394  * Create a new Editor
13395  * @param {Roo.form.Field} field The Field object (or descendant)
13396  * @param {Object} config The config object
13397  */
13398 Roo.Editor = function(field, config){
13399     Roo.Editor.superclass.constructor.call(this, config);
13400     this.field = field;
13401     this.addEvents({
13402         /**
13403              * @event beforestartedit
13404              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13405              * false from the handler of this event.
13406              * @param {Editor} this
13407              * @param {Roo.Element} boundEl The underlying element bound to this editor
13408              * @param {Mixed} value The field value being set
13409              */
13410         "beforestartedit" : true,
13411         /**
13412              * @event startedit
13413              * Fires when this editor is displayed
13414              * @param {Roo.Element} boundEl The underlying element bound to this editor
13415              * @param {Mixed} value The starting field value
13416              */
13417         "startedit" : true,
13418         /**
13419              * @event beforecomplete
13420              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13421              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13422              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13423              * event will not fire since no edit actually occurred.
13424              * @param {Editor} this
13425              * @param {Mixed} value The current field value
13426              * @param {Mixed} startValue The original field value
13427              */
13428         "beforecomplete" : true,
13429         /**
13430              * @event complete
13431              * Fires after editing is complete and any changed value has been written to the underlying field.
13432              * @param {Editor} this
13433              * @param {Mixed} value The current field value
13434              * @param {Mixed} startValue The original field value
13435              */
13436         "complete" : true,
13437         /**
13438          * @event specialkey
13439          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13440          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13441          * @param {Roo.form.Field} this
13442          * @param {Roo.EventObject} e The event object
13443          */
13444         "specialkey" : true
13445     });
13446 };
13447
13448 Roo.extend(Roo.Editor, Roo.Component, {
13449     /**
13450      * @cfg {Boolean/String} autosize
13451      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13452      * or "height" to adopt the height only (defaults to false)
13453      */
13454     /**
13455      * @cfg {Boolean} revertInvalid
13456      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13457      * validation fails (defaults to true)
13458      */
13459     /**
13460      * @cfg {Boolean} ignoreNoChange
13461      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13462      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13463      * will never be ignored.
13464      */
13465     /**
13466      * @cfg {Boolean} hideEl
13467      * False to keep the bound element visible while the editor is displayed (defaults to true)
13468      */
13469     /**
13470      * @cfg {Mixed} value
13471      * The data value of the underlying field (defaults to "")
13472      */
13473     value : "",
13474     /**
13475      * @cfg {String} alignment
13476      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13477      */
13478     alignment: "c-c?",
13479     /**
13480      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13481      * for bottom-right shadow (defaults to "frame")
13482      */
13483     shadow : "frame",
13484     /**
13485      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13486      */
13487     constrain : false,
13488     /**
13489      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13490      */
13491     completeOnEnter : false,
13492     /**
13493      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13494      */
13495     cancelOnEsc : false,
13496     /**
13497      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13498      */
13499     updateEl : false,
13500
13501     // private
13502     onRender : function(ct, position){
13503         this.el = new Roo.Layer({
13504             shadow: this.shadow,
13505             cls: "x-editor",
13506             parentEl : ct,
13507             shim : this.shim,
13508             shadowOffset:4,
13509             id: this.id,
13510             constrain: this.constrain
13511         });
13512         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13513         if(this.field.msgTarget != 'title'){
13514             this.field.msgTarget = 'qtip';
13515         }
13516         this.field.render(this.el);
13517         if(Roo.isGecko){
13518             this.field.el.dom.setAttribute('autocomplete', 'off');
13519         }
13520         this.field.on("specialkey", this.onSpecialKey, this);
13521         if(this.swallowKeys){
13522             this.field.el.swallowEvent(['keydown','keypress']);
13523         }
13524         this.field.show();
13525         this.field.on("blur", this.onBlur, this);
13526         if(this.field.grow){
13527             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13528         }
13529     },
13530
13531     onSpecialKey : function(field, e)
13532     {
13533         //Roo.log('editor onSpecialKey');
13534         if(this.completeOnEnter && e.getKey() == e.ENTER){
13535             e.stopEvent();
13536             this.completeEdit();
13537             return;
13538         }
13539         // do not fire special key otherwise it might hide close the editor...
13540         if(e.getKey() == e.ENTER){    
13541             return;
13542         }
13543         if(this.cancelOnEsc && e.getKey() == e.ESC){
13544             this.cancelEdit();
13545             return;
13546         } 
13547         this.fireEvent('specialkey', field, e);
13548     
13549     },
13550
13551     /**
13552      * Starts the editing process and shows the editor.
13553      * @param {String/HTMLElement/Element} el The element to edit
13554      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13555       * to the innerHTML of el.
13556      */
13557     startEdit : function(el, value){
13558         if(this.editing){
13559             this.completeEdit();
13560         }
13561         this.boundEl = Roo.get(el);
13562         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13563         if(!this.rendered){
13564             this.render(this.parentEl || document.body);
13565         }
13566         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13567             return;
13568         }
13569         this.startValue = v;
13570         this.field.setValue(v);
13571         if(this.autoSize){
13572             var sz = this.boundEl.getSize();
13573             switch(this.autoSize){
13574                 case "width":
13575                 this.setSize(sz.width,  "");
13576                 break;
13577                 case "height":
13578                 this.setSize("",  sz.height);
13579                 break;
13580                 default:
13581                 this.setSize(sz.width,  sz.height);
13582             }
13583         }
13584         this.el.alignTo(this.boundEl, this.alignment);
13585         this.editing = true;
13586         if(Roo.QuickTips){
13587             Roo.QuickTips.disable();
13588         }
13589         this.show();
13590     },
13591
13592     /**
13593      * Sets the height and width of this editor.
13594      * @param {Number} width The new width
13595      * @param {Number} height The new height
13596      */
13597     setSize : function(w, h){
13598         this.field.setSize(w, h);
13599         if(this.el){
13600             this.el.sync();
13601         }
13602     },
13603
13604     /**
13605      * Realigns the editor to the bound field based on the current alignment config value.
13606      */
13607     realign : function(){
13608         this.el.alignTo(this.boundEl, this.alignment);
13609     },
13610
13611     /**
13612      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13613      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13614      */
13615     completeEdit : function(remainVisible){
13616         if(!this.editing){
13617             return;
13618         }
13619         var v = this.getValue();
13620         if(this.revertInvalid !== false && !this.field.isValid()){
13621             v = this.startValue;
13622             this.cancelEdit(true);
13623         }
13624         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13625             this.editing = false;
13626             this.hide();
13627             return;
13628         }
13629         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13630             this.editing = false;
13631             if(this.updateEl && this.boundEl){
13632                 this.boundEl.update(v);
13633             }
13634             if(remainVisible !== true){
13635                 this.hide();
13636             }
13637             this.fireEvent("complete", this, v, this.startValue);
13638         }
13639     },
13640
13641     // private
13642     onShow : function(){
13643         this.el.show();
13644         if(this.hideEl !== false){
13645             this.boundEl.hide();
13646         }
13647         this.field.show();
13648         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13649             this.fixIEFocus = true;
13650             this.deferredFocus.defer(50, this);
13651         }else{
13652             this.field.focus();
13653         }
13654         this.fireEvent("startedit", this.boundEl, this.startValue);
13655     },
13656
13657     deferredFocus : function(){
13658         if(this.editing){
13659             this.field.focus();
13660         }
13661     },
13662
13663     /**
13664      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13665      * reverted to the original starting value.
13666      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13667      * cancel (defaults to false)
13668      */
13669     cancelEdit : function(remainVisible){
13670         if(this.editing){
13671             this.setValue(this.startValue);
13672             if(remainVisible !== true){
13673                 this.hide();
13674             }
13675         }
13676     },
13677
13678     // private
13679     onBlur : function(){
13680         if(this.allowBlur !== true && this.editing){
13681             this.completeEdit();
13682         }
13683     },
13684
13685     // private
13686     onHide : function(){
13687         if(this.editing){
13688             this.completeEdit();
13689             return;
13690         }
13691         this.field.blur();
13692         if(this.field.collapse){
13693             this.field.collapse();
13694         }
13695         this.el.hide();
13696         if(this.hideEl !== false){
13697             this.boundEl.show();
13698         }
13699         if(Roo.QuickTips){
13700             Roo.QuickTips.enable();
13701         }
13702     },
13703
13704     /**
13705      * Sets the data value of the editor
13706      * @param {Mixed} value Any valid value supported by the underlying field
13707      */
13708     setValue : function(v){
13709         this.field.setValue(v);
13710     },
13711
13712     /**
13713      * Gets the data value of the editor
13714      * @return {Mixed} The data value
13715      */
13716     getValue : function(){
13717         return this.field.getValue();
13718     }
13719 });/*
13720  * Based on:
13721  * Ext JS Library 1.1.1
13722  * Copyright(c) 2006-2007, Ext JS, LLC.
13723  *
13724  * Originally Released Under LGPL - original licence link has changed is not relivant.
13725  *
13726  * Fork - LGPL
13727  * <script type="text/javascript">
13728  */
13729  
13730 /**
13731  * @class Roo.BasicDialog
13732  * @extends Roo.util.Observable
13733  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13734  * <pre><code>
13735 var dlg = new Roo.BasicDialog("my-dlg", {
13736     height: 200,
13737     width: 300,
13738     minHeight: 100,
13739     minWidth: 150,
13740     modal: true,
13741     proxyDrag: true,
13742     shadow: true
13743 });
13744 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13745 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13746 dlg.addButton('Cancel', dlg.hide, dlg);
13747 dlg.show();
13748 </code></pre>
13749   <b>A Dialog should always be a direct child of the body element.</b>
13750  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13751  * @cfg {String} title Default text to display in the title bar (defaults to null)
13752  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13753  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13754  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13755  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13756  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13757  * (defaults to null with no animation)
13758  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13759  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13760  * property for valid values (defaults to 'all')
13761  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13762  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13763  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13764  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13765  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13766  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13767  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13768  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13769  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13770  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13771  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13772  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13773  * draggable = true (defaults to false)
13774  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13775  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13776  * shadow (defaults to false)
13777  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13778  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13779  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13780  * @cfg {Array} buttons Array of buttons
13781  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13782  * @constructor
13783  * Create a new BasicDialog.
13784  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13785  * @param {Object} config Configuration options
13786  */
13787 Roo.BasicDialog = function(el, config){
13788     this.el = Roo.get(el);
13789     var dh = Roo.DomHelper;
13790     if(!this.el && config && config.autoCreate){
13791         if(typeof config.autoCreate == "object"){
13792             if(!config.autoCreate.id){
13793                 config.autoCreate.id = el;
13794             }
13795             this.el = dh.append(document.body,
13796                         config.autoCreate, true);
13797         }else{
13798             this.el = dh.append(document.body,
13799                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13800         }
13801     }
13802     el = this.el;
13803     el.setDisplayed(true);
13804     el.hide = this.hideAction;
13805     this.id = el.id;
13806     el.addClass("x-dlg");
13807
13808     Roo.apply(this, config);
13809
13810     this.proxy = el.createProxy("x-dlg-proxy");
13811     this.proxy.hide = this.hideAction;
13812     this.proxy.setOpacity(.5);
13813     this.proxy.hide();
13814
13815     if(config.width){
13816         el.setWidth(config.width);
13817     }
13818     if(config.height){
13819         el.setHeight(config.height);
13820     }
13821     this.size = el.getSize();
13822     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13823         this.xy = [config.x,config.y];
13824     }else{
13825         this.xy = el.getCenterXY(true);
13826     }
13827     /** The header element @type Roo.Element */
13828     this.header = el.child("> .x-dlg-hd");
13829     /** The body element @type Roo.Element */
13830     this.body = el.child("> .x-dlg-bd");
13831     /** The footer element @type Roo.Element */
13832     this.footer = el.child("> .x-dlg-ft");
13833
13834     if(!this.header){
13835         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13836     }
13837     if(!this.body){
13838         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13839     }
13840
13841     this.header.unselectable();
13842     if(this.title){
13843         this.header.update(this.title);
13844     }
13845     // this element allows the dialog to be focused for keyboard event
13846     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13847     this.focusEl.swallowEvent("click", true);
13848
13849     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13850
13851     // wrap the body and footer for special rendering
13852     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13853     if(this.footer){
13854         this.bwrap.dom.appendChild(this.footer.dom);
13855     }
13856
13857     this.bg = this.el.createChild({
13858         tag: "div", cls:"x-dlg-bg",
13859         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13860     });
13861     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13862
13863
13864     if(this.autoScroll !== false && !this.autoTabs){
13865         this.body.setStyle("overflow", "auto");
13866     }
13867
13868     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13869
13870     if(this.closable !== false){
13871         this.el.addClass("x-dlg-closable");
13872         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13873         this.close.on("click", this.closeClick, this);
13874         this.close.addClassOnOver("x-dlg-close-over");
13875     }
13876     if(this.collapsible !== false){
13877         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13878         this.collapseBtn.on("click", this.collapseClick, this);
13879         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13880         this.header.on("dblclick", this.collapseClick, this);
13881     }
13882     if(this.resizable !== false){
13883         this.el.addClass("x-dlg-resizable");
13884         this.resizer = new Roo.Resizable(el, {
13885             minWidth: this.minWidth || 80,
13886             minHeight:this.minHeight || 80,
13887             handles: this.resizeHandles || "all",
13888             pinned: true
13889         });
13890         this.resizer.on("beforeresize", this.beforeResize, this);
13891         this.resizer.on("resize", this.onResize, this);
13892     }
13893     if(this.draggable !== false){
13894         el.addClass("x-dlg-draggable");
13895         if (!this.proxyDrag) {
13896             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13897         }
13898         else {
13899             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13900         }
13901         dd.setHandleElId(this.header.id);
13902         dd.endDrag = this.endMove.createDelegate(this);
13903         dd.startDrag = this.startMove.createDelegate(this);
13904         dd.onDrag = this.onDrag.createDelegate(this);
13905         dd.scroll = false;
13906         this.dd = dd;
13907     }
13908     if(this.modal){
13909         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13910         this.mask.enableDisplayMode("block");
13911         this.mask.hide();
13912         this.el.addClass("x-dlg-modal");
13913     }
13914     if(this.shadow){
13915         this.shadow = new Roo.Shadow({
13916             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13917             offset : this.shadowOffset
13918         });
13919     }else{
13920         this.shadowOffset = 0;
13921     }
13922     if(Roo.useShims && this.shim !== false){
13923         this.shim = this.el.createShim();
13924         this.shim.hide = this.hideAction;
13925         this.shim.hide();
13926     }else{
13927         this.shim = false;
13928     }
13929     if(this.autoTabs){
13930         this.initTabs();
13931     }
13932     if (this.buttons) { 
13933         var bts= this.buttons;
13934         this.buttons = [];
13935         Roo.each(bts, function(b) {
13936             this.addButton(b);
13937         }, this);
13938     }
13939     
13940     
13941     this.addEvents({
13942         /**
13943          * @event keydown
13944          * Fires when a key is pressed
13945          * @param {Roo.BasicDialog} this
13946          * @param {Roo.EventObject} e
13947          */
13948         "keydown" : true,
13949         /**
13950          * @event move
13951          * Fires when this dialog is moved by the user.
13952          * @param {Roo.BasicDialog} this
13953          * @param {Number} x The new page X
13954          * @param {Number} y The new page Y
13955          */
13956         "move" : true,
13957         /**
13958          * @event resize
13959          * Fires when this dialog is resized by the user.
13960          * @param {Roo.BasicDialog} this
13961          * @param {Number} width The new width
13962          * @param {Number} height The new height
13963          */
13964         "resize" : true,
13965         /**
13966          * @event beforehide
13967          * Fires before this dialog is hidden.
13968          * @param {Roo.BasicDialog} this
13969          */
13970         "beforehide" : true,
13971         /**
13972          * @event hide
13973          * Fires when this dialog is hidden.
13974          * @param {Roo.BasicDialog} this
13975          */
13976         "hide" : true,
13977         /**
13978          * @event beforeshow
13979          * Fires before this dialog is shown.
13980          * @param {Roo.BasicDialog} this
13981          */
13982         "beforeshow" : true,
13983         /**
13984          * @event show
13985          * Fires when this dialog is shown.
13986          * @param {Roo.BasicDialog} this
13987          */
13988         "show" : true
13989     });
13990     el.on("keydown", this.onKeyDown, this);
13991     el.on("mousedown", this.toFront, this);
13992     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13993     this.el.hide();
13994     Roo.DialogManager.register(this);
13995     Roo.BasicDialog.superclass.constructor.call(this);
13996 };
13997
13998 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13999     shadowOffset: Roo.isIE ? 6 : 5,
14000     minHeight: 80,
14001     minWidth: 200,
14002     minButtonWidth: 75,
14003     defaultButton: null,
14004     buttonAlign: "right",
14005     tabTag: 'div',
14006     firstShow: true,
14007
14008     /**
14009      * Sets the dialog title text
14010      * @param {String} text The title text to display
14011      * @return {Roo.BasicDialog} this
14012      */
14013     setTitle : function(text){
14014         this.header.update(text);
14015         return this;
14016     },
14017
14018     // private
14019     closeClick : function(){
14020         this.hide();
14021     },
14022
14023     // private
14024     collapseClick : function(){
14025         this[this.collapsed ? "expand" : "collapse"]();
14026     },
14027
14028     /**
14029      * Collapses the dialog to its minimized state (only the title bar is visible).
14030      * Equivalent to the user clicking the collapse dialog button.
14031      */
14032     collapse : function(){
14033         if(!this.collapsed){
14034             this.collapsed = true;
14035             this.el.addClass("x-dlg-collapsed");
14036             this.restoreHeight = this.el.getHeight();
14037             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14038         }
14039     },
14040
14041     /**
14042      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14043      * clicking the expand dialog button.
14044      */
14045     expand : function(){
14046         if(this.collapsed){
14047             this.collapsed = false;
14048             this.el.removeClass("x-dlg-collapsed");
14049             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14050         }
14051     },
14052
14053     /**
14054      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14055      * @return {Roo.TabPanel} The tabs component
14056      */
14057     initTabs : function(){
14058         var tabs = this.getTabs();
14059         while(tabs.getTab(0)){
14060             tabs.removeTab(0);
14061         }
14062         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14063             var dom = el.dom;
14064             tabs.addTab(Roo.id(dom), dom.title);
14065             dom.title = "";
14066         });
14067         tabs.activate(0);
14068         return tabs;
14069     },
14070
14071     // private
14072     beforeResize : function(){
14073         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14074     },
14075
14076     // private
14077     onResize : function(){
14078         this.refreshSize();
14079         this.syncBodyHeight();
14080         this.adjustAssets();
14081         this.focus();
14082         this.fireEvent("resize", this, this.size.width, this.size.height);
14083     },
14084
14085     // private
14086     onKeyDown : function(e){
14087         if(this.isVisible()){
14088             this.fireEvent("keydown", this, e);
14089         }
14090     },
14091
14092     /**
14093      * Resizes the dialog.
14094      * @param {Number} width
14095      * @param {Number} height
14096      * @return {Roo.BasicDialog} this
14097      */
14098     resizeTo : function(width, height){
14099         this.el.setSize(width, height);
14100         this.size = {width: width, height: height};
14101         this.syncBodyHeight();
14102         if(this.fixedcenter){
14103             this.center();
14104         }
14105         if(this.isVisible()){
14106             this.constrainXY();
14107             this.adjustAssets();
14108         }
14109         this.fireEvent("resize", this, width, height);
14110         return this;
14111     },
14112
14113
14114     /**
14115      * Resizes the dialog to fit the specified content size.
14116      * @param {Number} width
14117      * @param {Number} height
14118      * @return {Roo.BasicDialog} this
14119      */
14120     setContentSize : function(w, h){
14121         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14122         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14123         //if(!this.el.isBorderBox()){
14124             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14125             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14126         //}
14127         if(this.tabs){
14128             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14129             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14130         }
14131         this.resizeTo(w, h);
14132         return this;
14133     },
14134
14135     /**
14136      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14137      * executed in response to a particular key being pressed while the dialog is active.
14138      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14139      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14140      * @param {Function} fn The function to call
14141      * @param {Object} scope (optional) The scope of the function
14142      * @return {Roo.BasicDialog} this
14143      */
14144     addKeyListener : function(key, fn, scope){
14145         var keyCode, shift, ctrl, alt;
14146         if(typeof key == "object" && !(key instanceof Array)){
14147             keyCode = key["key"];
14148             shift = key["shift"];
14149             ctrl = key["ctrl"];
14150             alt = key["alt"];
14151         }else{
14152             keyCode = key;
14153         }
14154         var handler = function(dlg, e){
14155             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14156                 var k = e.getKey();
14157                 if(keyCode instanceof Array){
14158                     for(var i = 0, len = keyCode.length; i < len; i++){
14159                         if(keyCode[i] == k){
14160                           fn.call(scope || window, dlg, k, e);
14161                           return;
14162                         }
14163                     }
14164                 }else{
14165                     if(k == keyCode){
14166                         fn.call(scope || window, dlg, k, e);
14167                     }
14168                 }
14169             }
14170         };
14171         this.on("keydown", handler);
14172         return this;
14173     },
14174
14175     /**
14176      * Returns the TabPanel component (creates it if it doesn't exist).
14177      * Note: If you wish to simply check for the existence of tabs without creating them,
14178      * check for a null 'tabs' property.
14179      * @return {Roo.TabPanel} The tabs component
14180      */
14181     getTabs : function(){
14182         if(!this.tabs){
14183             this.el.addClass("x-dlg-auto-tabs");
14184             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14185             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14186         }
14187         return this.tabs;
14188     },
14189
14190     /**
14191      * Adds a button to the footer section of the dialog.
14192      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14193      * object or a valid Roo.DomHelper element config
14194      * @param {Function} handler The function called when the button is clicked
14195      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14196      * @return {Roo.Button} The new button
14197      */
14198     addButton : function(config, handler, scope){
14199         var dh = Roo.DomHelper;
14200         if(!this.footer){
14201             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14202         }
14203         if(!this.btnContainer){
14204             var tb = this.footer.createChild({
14205
14206                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14207                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14208             }, null, true);
14209             this.btnContainer = tb.firstChild.firstChild.firstChild;
14210         }
14211         var bconfig = {
14212             handler: handler,
14213             scope: scope,
14214             minWidth: this.minButtonWidth,
14215             hideParent:true
14216         };
14217         if(typeof config == "string"){
14218             bconfig.text = config;
14219         }else{
14220             if(config.tag){
14221                 bconfig.dhconfig = config;
14222             }else{
14223                 Roo.apply(bconfig, config);
14224             }
14225         }
14226         var fc = false;
14227         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14228             bconfig.position = Math.max(0, bconfig.position);
14229             fc = this.btnContainer.childNodes[bconfig.position];
14230         }
14231          
14232         var btn = new Roo.Button(
14233             fc ? 
14234                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14235                 : this.btnContainer.appendChild(document.createElement("td")),
14236             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14237             bconfig
14238         );
14239         this.syncBodyHeight();
14240         if(!this.buttons){
14241             /**
14242              * Array of all the buttons that have been added to this dialog via addButton
14243              * @type Array
14244              */
14245             this.buttons = [];
14246         }
14247         this.buttons.push(btn);
14248         return btn;
14249     },
14250
14251     /**
14252      * Sets the default button to be focused when the dialog is displayed.
14253      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14254      * @return {Roo.BasicDialog} this
14255      */
14256     setDefaultButton : function(btn){
14257         this.defaultButton = btn;
14258         return this;
14259     },
14260
14261     // private
14262     getHeaderFooterHeight : function(safe){
14263         var height = 0;
14264         if(this.header){
14265            height += this.header.getHeight();
14266         }
14267         if(this.footer){
14268            var fm = this.footer.getMargins();
14269             height += (this.footer.getHeight()+fm.top+fm.bottom);
14270         }
14271         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14272         height += this.centerBg.getPadding("tb");
14273         return height;
14274     },
14275
14276     // private
14277     syncBodyHeight : function()
14278     {
14279         var bd = this.body, // the text
14280             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14281             bw = this.bwrap;
14282         var height = this.size.height - this.getHeaderFooterHeight(false);
14283         bd.setHeight(height-bd.getMargins("tb"));
14284         var hh = this.header.getHeight();
14285         var h = this.size.height-hh;
14286         cb.setHeight(h);
14287         
14288         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14289         bw.setHeight(h-cb.getPadding("tb"));
14290         
14291         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14292         bd.setWidth(bw.getWidth(true));
14293         if(this.tabs){
14294             this.tabs.syncHeight();
14295             if(Roo.isIE){
14296                 this.tabs.el.repaint();
14297             }
14298         }
14299     },
14300
14301     /**
14302      * Restores the previous state of the dialog if Roo.state is configured.
14303      * @return {Roo.BasicDialog} this
14304      */
14305     restoreState : function(){
14306         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14307         if(box && box.width){
14308             this.xy = [box.x, box.y];
14309             this.resizeTo(box.width, box.height);
14310         }
14311         return this;
14312     },
14313
14314     // private
14315     beforeShow : function(){
14316         this.expand();
14317         if(this.fixedcenter){
14318             this.xy = this.el.getCenterXY(true);
14319         }
14320         if(this.modal){
14321             Roo.get(document.body).addClass("x-body-masked");
14322             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14323             this.mask.show();
14324         }
14325         this.constrainXY();
14326     },
14327
14328     // private
14329     animShow : function(){
14330         var b = Roo.get(this.animateTarget).getBox();
14331         this.proxy.setSize(b.width, b.height);
14332         this.proxy.setLocation(b.x, b.y);
14333         this.proxy.show();
14334         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14335                     true, .35, this.showEl.createDelegate(this));
14336     },
14337
14338     /**
14339      * Shows the dialog.
14340      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14341      * @return {Roo.BasicDialog} this
14342      */
14343     show : function(animateTarget){
14344         if (this.fireEvent("beforeshow", this) === false){
14345             return;
14346         }
14347         if(this.syncHeightBeforeShow){
14348             this.syncBodyHeight();
14349         }else if(this.firstShow){
14350             this.firstShow = false;
14351             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14352         }
14353         this.animateTarget = animateTarget || this.animateTarget;
14354         if(!this.el.isVisible()){
14355             this.beforeShow();
14356             if(this.animateTarget && Roo.get(this.animateTarget)){
14357                 this.animShow();
14358             }else{
14359                 this.showEl();
14360             }
14361         }
14362         return this;
14363     },
14364
14365     // private
14366     showEl : function(){
14367         this.proxy.hide();
14368         this.el.setXY(this.xy);
14369         this.el.show();
14370         this.adjustAssets(true);
14371         this.toFront();
14372         this.focus();
14373         // IE peekaboo bug - fix found by Dave Fenwick
14374         if(Roo.isIE){
14375             this.el.repaint();
14376         }
14377         this.fireEvent("show", this);
14378     },
14379
14380     /**
14381      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14382      * dialog itself will receive focus.
14383      */
14384     focus : function(){
14385         if(this.defaultButton){
14386             this.defaultButton.focus();
14387         }else{
14388             this.focusEl.focus();
14389         }
14390     },
14391
14392     // private
14393     constrainXY : function(){
14394         if(this.constraintoviewport !== false){
14395             if(!this.viewSize){
14396                 if(this.container){
14397                     var s = this.container.getSize();
14398                     this.viewSize = [s.width, s.height];
14399                 }else{
14400                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14401                 }
14402             }
14403             var s = Roo.get(this.container||document).getScroll();
14404
14405             var x = this.xy[0], y = this.xy[1];
14406             var w = this.size.width, h = this.size.height;
14407             var vw = this.viewSize[0], vh = this.viewSize[1];
14408             // only move it if it needs it
14409             var moved = false;
14410             // first validate right/bottom
14411             if(x + w > vw+s.left){
14412                 x = vw - w;
14413                 moved = true;
14414             }
14415             if(y + h > vh+s.top){
14416                 y = vh - h;
14417                 moved = true;
14418             }
14419             // then make sure top/left isn't negative
14420             if(x < s.left){
14421                 x = s.left;
14422                 moved = true;
14423             }
14424             if(y < s.top){
14425                 y = s.top;
14426                 moved = true;
14427             }
14428             if(moved){
14429                 // cache xy
14430                 this.xy = [x, y];
14431                 if(this.isVisible()){
14432                     this.el.setLocation(x, y);
14433                     this.adjustAssets();
14434                 }
14435             }
14436         }
14437     },
14438
14439     // private
14440     onDrag : function(){
14441         if(!this.proxyDrag){
14442             this.xy = this.el.getXY();
14443             this.adjustAssets();
14444         }
14445     },
14446
14447     // private
14448     adjustAssets : function(doShow){
14449         var x = this.xy[0], y = this.xy[1];
14450         var w = this.size.width, h = this.size.height;
14451         if(doShow === true){
14452             if(this.shadow){
14453                 this.shadow.show(this.el);
14454             }
14455             if(this.shim){
14456                 this.shim.show();
14457             }
14458         }
14459         if(this.shadow && this.shadow.isVisible()){
14460             this.shadow.show(this.el);
14461         }
14462         if(this.shim && this.shim.isVisible()){
14463             this.shim.setBounds(x, y, w, h);
14464         }
14465     },
14466
14467     // private
14468     adjustViewport : function(w, h){
14469         if(!w || !h){
14470             w = Roo.lib.Dom.getViewWidth();
14471             h = Roo.lib.Dom.getViewHeight();
14472         }
14473         // cache the size
14474         this.viewSize = [w, h];
14475         if(this.modal && this.mask.isVisible()){
14476             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14477             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14478         }
14479         if(this.isVisible()){
14480             this.constrainXY();
14481         }
14482     },
14483
14484     /**
14485      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14486      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14487      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14488      */
14489     destroy : function(removeEl){
14490         if(this.isVisible()){
14491             this.animateTarget = null;
14492             this.hide();
14493         }
14494         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14495         if(this.tabs){
14496             this.tabs.destroy(removeEl);
14497         }
14498         Roo.destroy(
14499              this.shim,
14500              this.proxy,
14501              this.resizer,
14502              this.close,
14503              this.mask
14504         );
14505         if(this.dd){
14506             this.dd.unreg();
14507         }
14508         if(this.buttons){
14509            for(var i = 0, len = this.buttons.length; i < len; i++){
14510                this.buttons[i].destroy();
14511            }
14512         }
14513         this.el.removeAllListeners();
14514         if(removeEl === true){
14515             this.el.update("");
14516             this.el.remove();
14517         }
14518         Roo.DialogManager.unregister(this);
14519     },
14520
14521     // private
14522     startMove : function(){
14523         if(this.proxyDrag){
14524             this.proxy.show();
14525         }
14526         if(this.constraintoviewport !== false){
14527             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14528         }
14529     },
14530
14531     // private
14532     endMove : function(){
14533         if(!this.proxyDrag){
14534             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14535         }else{
14536             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14537             this.proxy.hide();
14538         }
14539         this.refreshSize();
14540         this.adjustAssets();
14541         this.focus();
14542         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14543     },
14544
14545     /**
14546      * Brings this dialog to the front of any other visible dialogs
14547      * @return {Roo.BasicDialog} this
14548      */
14549     toFront : function(){
14550         Roo.DialogManager.bringToFront(this);
14551         return this;
14552     },
14553
14554     /**
14555      * Sends this dialog to the back (under) of any other visible dialogs
14556      * @return {Roo.BasicDialog} this
14557      */
14558     toBack : function(){
14559         Roo.DialogManager.sendToBack(this);
14560         return this;
14561     },
14562
14563     /**
14564      * Centers this dialog in the viewport
14565      * @return {Roo.BasicDialog} this
14566      */
14567     center : function(){
14568         var xy = this.el.getCenterXY(true);
14569         this.moveTo(xy[0], xy[1]);
14570         return this;
14571     },
14572
14573     /**
14574      * Moves the dialog's top-left corner to the specified point
14575      * @param {Number} x
14576      * @param {Number} y
14577      * @return {Roo.BasicDialog} this
14578      */
14579     moveTo : function(x, y){
14580         this.xy = [x,y];
14581         if(this.isVisible()){
14582             this.el.setXY(this.xy);
14583             this.adjustAssets();
14584         }
14585         return this;
14586     },
14587
14588     /**
14589      * Aligns the dialog to the specified element
14590      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14591      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14592      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14593      * @return {Roo.BasicDialog} this
14594      */
14595     alignTo : function(element, position, offsets){
14596         this.xy = this.el.getAlignToXY(element, position, offsets);
14597         if(this.isVisible()){
14598             this.el.setXY(this.xy);
14599             this.adjustAssets();
14600         }
14601         return this;
14602     },
14603
14604     /**
14605      * Anchors an element to another element and realigns it when the window is resized.
14606      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14607      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14608      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14609      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14610      * is a number, it is used as the buffer delay (defaults to 50ms).
14611      * @return {Roo.BasicDialog} this
14612      */
14613     anchorTo : function(el, alignment, offsets, monitorScroll){
14614         var action = function(){
14615             this.alignTo(el, alignment, offsets);
14616         };
14617         Roo.EventManager.onWindowResize(action, this);
14618         var tm = typeof monitorScroll;
14619         if(tm != 'undefined'){
14620             Roo.EventManager.on(window, 'scroll', action, this,
14621                 {buffer: tm == 'number' ? monitorScroll : 50});
14622         }
14623         action.call(this);
14624         return this;
14625     },
14626
14627     /**
14628      * Returns true if the dialog is visible
14629      * @return {Boolean}
14630      */
14631     isVisible : function(){
14632         return this.el.isVisible();
14633     },
14634
14635     // private
14636     animHide : function(callback){
14637         var b = Roo.get(this.animateTarget).getBox();
14638         this.proxy.show();
14639         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14640         this.el.hide();
14641         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14642                     this.hideEl.createDelegate(this, [callback]));
14643     },
14644
14645     /**
14646      * Hides the dialog.
14647      * @param {Function} callback (optional) Function to call when the dialog is hidden
14648      * @return {Roo.BasicDialog} this
14649      */
14650     hide : function(callback){
14651         if (this.fireEvent("beforehide", this) === false){
14652             return;
14653         }
14654         if(this.shadow){
14655             this.shadow.hide();
14656         }
14657         if(this.shim) {
14658           this.shim.hide();
14659         }
14660         // sometimes animateTarget seems to get set.. causing problems...
14661         // this just double checks..
14662         if(this.animateTarget && Roo.get(this.animateTarget)) {
14663            this.animHide(callback);
14664         }else{
14665             this.el.hide();
14666             this.hideEl(callback);
14667         }
14668         return this;
14669     },
14670
14671     // private
14672     hideEl : function(callback){
14673         this.proxy.hide();
14674         if(this.modal){
14675             this.mask.hide();
14676             Roo.get(document.body).removeClass("x-body-masked");
14677         }
14678         this.fireEvent("hide", this);
14679         if(typeof callback == "function"){
14680             callback();
14681         }
14682     },
14683
14684     // private
14685     hideAction : function(){
14686         this.setLeft("-10000px");
14687         this.setTop("-10000px");
14688         this.setStyle("visibility", "hidden");
14689     },
14690
14691     // private
14692     refreshSize : function(){
14693         this.size = this.el.getSize();
14694         this.xy = this.el.getXY();
14695         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14696     },
14697
14698     // private
14699     // z-index is managed by the DialogManager and may be overwritten at any time
14700     setZIndex : function(index){
14701         if(this.modal){
14702             this.mask.setStyle("z-index", index);
14703         }
14704         if(this.shim){
14705             this.shim.setStyle("z-index", ++index);
14706         }
14707         if(this.shadow){
14708             this.shadow.setZIndex(++index);
14709         }
14710         this.el.setStyle("z-index", ++index);
14711         if(this.proxy){
14712             this.proxy.setStyle("z-index", ++index);
14713         }
14714         if(this.resizer){
14715             this.resizer.proxy.setStyle("z-index", ++index);
14716         }
14717
14718         this.lastZIndex = index;
14719     },
14720
14721     /**
14722      * Returns the element for this dialog
14723      * @return {Roo.Element} The underlying dialog Element
14724      */
14725     getEl : function(){
14726         return this.el;
14727     }
14728 });
14729
14730 /**
14731  * @class Roo.DialogManager
14732  * Provides global access to BasicDialogs that have been created and
14733  * support for z-indexing (layering) multiple open dialogs.
14734  */
14735 Roo.DialogManager = function(){
14736     var list = {};
14737     var accessList = [];
14738     var front = null;
14739
14740     // private
14741     var sortDialogs = function(d1, d2){
14742         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14743     };
14744
14745     // private
14746     var orderDialogs = function(){
14747         accessList.sort(sortDialogs);
14748         var seed = Roo.DialogManager.zseed;
14749         for(var i = 0, len = accessList.length; i < len; i++){
14750             var dlg = accessList[i];
14751             if(dlg){
14752                 dlg.setZIndex(seed + (i*10));
14753             }
14754         }
14755     };
14756
14757     return {
14758         /**
14759          * The starting z-index for BasicDialogs (defaults to 9000)
14760          * @type Number The z-index value
14761          */
14762         zseed : 9000,
14763
14764         // private
14765         register : function(dlg){
14766             list[dlg.id] = dlg;
14767             accessList.push(dlg);
14768         },
14769
14770         // private
14771         unregister : function(dlg){
14772             delete list[dlg.id];
14773             var i=0;
14774             var len=0;
14775             if(!accessList.indexOf){
14776                 for(  i = 0, len = accessList.length; i < len; i++){
14777                     if(accessList[i] == dlg){
14778                         accessList.splice(i, 1);
14779                         return;
14780                     }
14781                 }
14782             }else{
14783                  i = accessList.indexOf(dlg);
14784                 if(i != -1){
14785                     accessList.splice(i, 1);
14786                 }
14787             }
14788         },
14789
14790         /**
14791          * Gets a registered dialog by id
14792          * @param {String/Object} id The id of the dialog or a dialog
14793          * @return {Roo.BasicDialog} this
14794          */
14795         get : function(id){
14796             return typeof id == "object" ? id : list[id];
14797         },
14798
14799         /**
14800          * Brings the specified dialog to the front
14801          * @param {String/Object} dlg The id of the dialog or a dialog
14802          * @return {Roo.BasicDialog} this
14803          */
14804         bringToFront : function(dlg){
14805             dlg = this.get(dlg);
14806             if(dlg != front){
14807                 front = dlg;
14808                 dlg._lastAccess = new Date().getTime();
14809                 orderDialogs();
14810             }
14811             return dlg;
14812         },
14813
14814         /**
14815          * Sends the specified dialog to the back
14816          * @param {String/Object} dlg The id of the dialog or a dialog
14817          * @return {Roo.BasicDialog} this
14818          */
14819         sendToBack : function(dlg){
14820             dlg = this.get(dlg);
14821             dlg._lastAccess = -(new Date().getTime());
14822             orderDialogs();
14823             return dlg;
14824         },
14825
14826         /**
14827          * Hides all dialogs
14828          */
14829         hideAll : function(){
14830             for(var id in list){
14831                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14832                     list[id].hide();
14833                 }
14834             }
14835         }
14836     };
14837 }();
14838
14839 /**
14840  * @class Roo.LayoutDialog
14841  * @extends Roo.BasicDialog
14842  * Dialog which provides adjustments for working with a layout in a Dialog.
14843  * Add your necessary layout config options to the dialog's config.<br>
14844  * Example usage (including a nested layout):
14845  * <pre><code>
14846 if(!dialog){
14847     dialog = new Roo.LayoutDialog("download-dlg", {
14848         modal: true,
14849         width:600,
14850         height:450,
14851         shadow:true,
14852         minWidth:500,
14853         minHeight:350,
14854         autoTabs:true,
14855         proxyDrag:true,
14856         // layout config merges with the dialog config
14857         center:{
14858             tabPosition: "top",
14859             alwaysShowTabs: true
14860         }
14861     });
14862     dialog.addKeyListener(27, dialog.hide, dialog);
14863     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14864     dialog.addButton("Build It!", this.getDownload, this);
14865
14866     // we can even add nested layouts
14867     var innerLayout = new Roo.BorderLayout("dl-inner", {
14868         east: {
14869             initialSize: 200,
14870             autoScroll:true,
14871             split:true
14872         },
14873         center: {
14874             autoScroll:true
14875         }
14876     });
14877     innerLayout.beginUpdate();
14878     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14879     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14880     innerLayout.endUpdate(true);
14881
14882     var layout = dialog.getLayout();
14883     layout.beginUpdate();
14884     layout.add("center", new Roo.ContentPanel("standard-panel",
14885                         {title: "Download the Source", fitToFrame:true}));
14886     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14887                {title: "Build your own roo.js"}));
14888     layout.getRegion("center").showPanel(sp);
14889     layout.endUpdate();
14890 }
14891 </code></pre>
14892     * @constructor
14893     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14894     * @param {Object} config configuration options
14895   */
14896 Roo.LayoutDialog = function(el, cfg){
14897     
14898     var config=  cfg;
14899     if (typeof(cfg) == 'undefined') {
14900         config = Roo.apply({}, el);
14901         // not sure why we use documentElement here.. - it should always be body.
14902         // IE7 borks horribly if we use documentElement.
14903         // webkit also does not like documentElement - it creates a body element...
14904         el = Roo.get( document.body || document.documentElement ).createChild();
14905         //config.autoCreate = true;
14906     }
14907     
14908     
14909     config.autoTabs = false;
14910     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14911     this.body.setStyle({overflow:"hidden", position:"relative"});
14912     this.layout = new Roo.BorderLayout(this.body.dom, config);
14913     this.layout.monitorWindowResize = false;
14914     this.el.addClass("x-dlg-auto-layout");
14915     // fix case when center region overwrites center function
14916     this.center = Roo.BasicDialog.prototype.center;
14917     this.on("show", this.layout.layout, this.layout, true);
14918     if (config.items) {
14919         var xitems = config.items;
14920         delete config.items;
14921         Roo.each(xitems, this.addxtype, this);
14922     }
14923     
14924     
14925 };
14926 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14927     /**
14928      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14929      * @deprecated
14930      */
14931     endUpdate : function(){
14932         this.layout.endUpdate();
14933     },
14934
14935     /**
14936      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14937      *  @deprecated
14938      */
14939     beginUpdate : function(){
14940         this.layout.beginUpdate();
14941     },
14942
14943     /**
14944      * Get the BorderLayout for this dialog
14945      * @return {Roo.BorderLayout}
14946      */
14947     getLayout : function(){
14948         return this.layout;
14949     },
14950
14951     showEl : function(){
14952         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14953         if(Roo.isIE7){
14954             this.layout.layout();
14955         }
14956     },
14957
14958     // private
14959     // Use the syncHeightBeforeShow config option to control this automatically
14960     syncBodyHeight : function(){
14961         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14962         if(this.layout){this.layout.layout();}
14963     },
14964     
14965       /**
14966      * Add an xtype element (actually adds to the layout.)
14967      * @return {Object} xdata xtype object data.
14968      */
14969     
14970     addxtype : function(c) {
14971         return this.layout.addxtype(c);
14972     }
14973 });/*
14974  * Based on:
14975  * Ext JS Library 1.1.1
14976  * Copyright(c) 2006-2007, Ext JS, LLC.
14977  *
14978  * Originally Released Under LGPL - original licence link has changed is not relivant.
14979  *
14980  * Fork - LGPL
14981  * <script type="text/javascript">
14982  */
14983  
14984 /**
14985  * @class Roo.MessageBox
14986  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14987  * Example usage:
14988  *<pre><code>
14989 // Basic alert:
14990 Roo.Msg.alert('Status', 'Changes saved successfully.');
14991
14992 // Prompt for user data:
14993 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14994     if (btn == 'ok'){
14995         // process text value...
14996     }
14997 });
14998
14999 // Show a dialog using config options:
15000 Roo.Msg.show({
15001    title:'Save Changes?',
15002    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15003    buttons: Roo.Msg.YESNOCANCEL,
15004    fn: processResult,
15005    animEl: 'elId'
15006 });
15007 </code></pre>
15008  * @singleton
15009  */
15010 Roo.MessageBox = function(){
15011     var dlg, opt, mask, waitTimer;
15012     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15013     var buttons, activeTextEl, bwidth;
15014
15015     // private
15016     var handleButton = function(button){
15017         dlg.hide();
15018         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15019     };
15020
15021     // private
15022     var handleHide = function(){
15023         if(opt && opt.cls){
15024             dlg.el.removeClass(opt.cls);
15025         }
15026         if(waitTimer){
15027             Roo.TaskMgr.stop(waitTimer);
15028             waitTimer = null;
15029         }
15030     };
15031
15032     // private
15033     var updateButtons = function(b){
15034         var width = 0;
15035         if(!b){
15036             buttons["ok"].hide();
15037             buttons["cancel"].hide();
15038             buttons["yes"].hide();
15039             buttons["no"].hide();
15040             dlg.footer.dom.style.display = 'none';
15041             return width;
15042         }
15043         dlg.footer.dom.style.display = '';
15044         for(var k in buttons){
15045             if(typeof buttons[k] != "function"){
15046                 if(b[k]){
15047                     buttons[k].show();
15048                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15049                     width += buttons[k].el.getWidth()+15;
15050                 }else{
15051                     buttons[k].hide();
15052                 }
15053             }
15054         }
15055         return width;
15056     };
15057
15058     // private
15059     var handleEsc = function(d, k, e){
15060         if(opt && opt.closable !== false){
15061             dlg.hide();
15062         }
15063         if(e){
15064             e.stopEvent();
15065         }
15066     };
15067
15068     return {
15069         /**
15070          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15071          * @return {Roo.BasicDialog} The BasicDialog element
15072          */
15073         getDialog : function(){
15074            if(!dlg){
15075                 dlg = new Roo.BasicDialog("x-msg-box", {
15076                     autoCreate : true,
15077                     shadow: true,
15078                     draggable: true,
15079                     resizable:false,
15080                     constraintoviewport:false,
15081                     fixedcenter:true,
15082                     collapsible : false,
15083                     shim:true,
15084                     modal: true,
15085                     width:400, height:100,
15086                     buttonAlign:"center",
15087                     closeClick : function(){
15088                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15089                             handleButton("no");
15090                         }else{
15091                             handleButton("cancel");
15092                         }
15093                     }
15094                 });
15095                 dlg.on("hide", handleHide);
15096                 mask = dlg.mask;
15097                 dlg.addKeyListener(27, handleEsc);
15098                 buttons = {};
15099                 var bt = this.buttonText;
15100                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15101                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15102                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15103                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15104                 bodyEl = dlg.body.createChild({
15105
15106                     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>'
15107                 });
15108                 msgEl = bodyEl.dom.firstChild;
15109                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15110                 textboxEl.enableDisplayMode();
15111                 textboxEl.addKeyListener([10,13], function(){
15112                     if(dlg.isVisible() && opt && opt.buttons){
15113                         if(opt.buttons.ok){
15114                             handleButton("ok");
15115                         }else if(opt.buttons.yes){
15116                             handleButton("yes");
15117                         }
15118                     }
15119                 });
15120                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15121                 textareaEl.enableDisplayMode();
15122                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15123                 progressEl.enableDisplayMode();
15124                 var pf = progressEl.dom.firstChild;
15125                 if (pf) {
15126                     pp = Roo.get(pf.firstChild);
15127                     pp.setHeight(pf.offsetHeight);
15128                 }
15129                 
15130             }
15131             return dlg;
15132         },
15133
15134         /**
15135          * Updates the message box body text
15136          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15137          * the XHTML-compliant non-breaking space character '&amp;#160;')
15138          * @return {Roo.MessageBox} This message box
15139          */
15140         updateText : function(text){
15141             if(!dlg.isVisible() && !opt.width){
15142                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15143             }
15144             msgEl.innerHTML = text || '&#160;';
15145       
15146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15148             var w = Math.max(
15149                     Math.min(opt.width || cw , this.maxWidth), 
15150                     Math.max(opt.minWidth || this.minWidth, bwidth)
15151             );
15152             if(opt.prompt){
15153                 activeTextEl.setWidth(w);
15154             }
15155             if(dlg.isVisible()){
15156                 dlg.fixedcenter = false;
15157             }
15158             // to big, make it scroll. = But as usual stupid IE does not support
15159             // !important..
15160             
15161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15164             } else {
15165                 bodyEl.dom.style.height = '';
15166                 bodyEl.dom.style.overflowY = '';
15167             }
15168             if (cw > w) {
15169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15170             } else {
15171                 bodyEl.dom.style.overflowX = '';
15172             }
15173             
15174             dlg.setContentSize(w, bodyEl.getHeight());
15175             if(dlg.isVisible()){
15176                 dlg.fixedcenter = true;
15177             }
15178             return this;
15179         },
15180
15181         /**
15182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15186          * @return {Roo.MessageBox} This message box
15187          */
15188         updateProgress : function(value, text){
15189             if(text){
15190                 this.updateText(text);
15191             }
15192             if (pp) { // weird bug on my firefox - for some reason this is not defined
15193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15194             }
15195             return this;
15196         },        
15197
15198         /**
15199          * Returns true if the message box is currently displayed
15200          * @return {Boolean} True if the message box is visible, else false
15201          */
15202         isVisible : function(){
15203             return dlg && dlg.isVisible();  
15204         },
15205
15206         /**
15207          * Hides the message box if it is displayed
15208          */
15209         hide : function(){
15210             if(this.isVisible()){
15211                 dlg.hide();
15212             }  
15213         },
15214
15215         /**
15216          * Displays a new message box, or reinitializes an existing message box, based on the config options
15217          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15218          * The following config object properties are supported:
15219          * <pre>
15220 Property    Type             Description
15221 ----------  ---------------  ------------------------------------------------------------------------------------
15222 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15223                                    closes (defaults to undefined)
15224 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15225                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15226 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15227                                    progress and wait dialogs will ignore this property and always hide the
15228                                    close button as they can only be closed programmatically.
15229 cls               String           A custom CSS class to apply to the message box element
15230 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15231                                    displayed (defaults to 75)
15232 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15233                                    function will be btn (the name of the button that was clicked, if applicable,
15234                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15235                                    Progress and wait dialogs will ignore this option since they do not respond to
15236                                    user actions and can only be closed programmatically, so any required function
15237                                    should be called by the same code after it closes the dialog.
15238 icon              String           A CSS class that provides a background image to be used as an icon for
15239                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15240 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15241 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15242 modal             Boolean          False to allow user interaction with the page while the message box is
15243                                    displayed (defaults to true)
15244 msg               String           A string that will replace the existing message box body text (defaults
15245                                    to the XHTML-compliant non-breaking space character '&#160;')
15246 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15247 progress          Boolean          True to display a progress bar (defaults to false)
15248 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15249 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15250 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15251 title             String           The title text
15252 value             String           The string value to set into the active textbox element if displayed
15253 wait              Boolean          True to display a progress bar (defaults to false)
15254 width             Number           The width of the dialog in pixels
15255 </pre>
15256          *
15257          * Example usage:
15258          * <pre><code>
15259 Roo.Msg.show({
15260    title: 'Address',
15261    msg: 'Please enter your address:',
15262    width: 300,
15263    buttons: Roo.MessageBox.OKCANCEL,
15264    multiline: true,
15265    fn: saveAddress,
15266    animEl: 'addAddressBtn'
15267 });
15268 </code></pre>
15269          * @param {Object} config Configuration options
15270          * @return {Roo.MessageBox} This message box
15271          */
15272         show : function(options)
15273         {
15274             
15275             // this causes nightmares if you show one dialog after another
15276             // especially on callbacks..
15277              
15278             if(this.isVisible()){
15279                 
15280                 this.hide();
15281                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15282                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15283                 Roo.log("New Dialog Message:" +  options.msg )
15284                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15285                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15286                 
15287             }
15288             var d = this.getDialog();
15289             opt = options;
15290             d.setTitle(opt.title || "&#160;");
15291             d.close.setDisplayed(opt.closable !== false);
15292             activeTextEl = textboxEl;
15293             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15294             if(opt.prompt){
15295                 if(opt.multiline){
15296                     textboxEl.hide();
15297                     textareaEl.show();
15298                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15299                         opt.multiline : this.defaultTextHeight);
15300                     activeTextEl = textareaEl;
15301                 }else{
15302                     textboxEl.show();
15303                     textareaEl.hide();
15304                 }
15305             }else{
15306                 textboxEl.hide();
15307                 textareaEl.hide();
15308             }
15309             progressEl.setDisplayed(opt.progress === true);
15310             this.updateProgress(0);
15311             activeTextEl.dom.value = opt.value || "";
15312             if(opt.prompt){
15313                 dlg.setDefaultButton(activeTextEl);
15314             }else{
15315                 var bs = opt.buttons;
15316                 var db = null;
15317                 if(bs && bs.ok){
15318                     db = buttons["ok"];
15319                 }else if(bs && bs.yes){
15320                     db = buttons["yes"];
15321                 }
15322                 dlg.setDefaultButton(db);
15323             }
15324             bwidth = updateButtons(opt.buttons);
15325             this.updateText(opt.msg);
15326             if(opt.cls){
15327                 d.el.addClass(opt.cls);
15328             }
15329             d.proxyDrag = opt.proxyDrag === true;
15330             d.modal = opt.modal !== false;
15331             d.mask = opt.modal !== false ? mask : false;
15332             if(!d.isVisible()){
15333                 // force it to the end of the z-index stack so it gets a cursor in FF
15334                 document.body.appendChild(dlg.el.dom);
15335                 d.animateTarget = null;
15336                 d.show(options.animEl);
15337             }
15338             return this;
15339         },
15340
15341         /**
15342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15344          * and closing the message box when the process is complete.
15345          * @param {String} title The title bar text
15346          * @param {String} msg The message box body text
15347          * @return {Roo.MessageBox} This message box
15348          */
15349         progress : function(title, msg){
15350             this.show({
15351                 title : title,
15352                 msg : msg,
15353                 buttons: false,
15354                 progress:true,
15355                 closable:false,
15356                 minWidth: this.minProgressWidth,
15357                 modal : true
15358             });
15359             return this;
15360         },
15361
15362         /**
15363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15364          * If a callback function is passed it will be called after the user clicks the button, and the
15365          * id of the button that was clicked will be passed as the only parameter to the callback
15366          * (could also be the top-right close button).
15367          * @param {String} title The title bar text
15368          * @param {String} msg The message box body text
15369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15370          * @param {Object} scope (optional) The scope of the callback function
15371          * @return {Roo.MessageBox} This message box
15372          */
15373         alert : function(title, msg, fn, scope){
15374             this.show({
15375                 title : title,
15376                 msg : msg,
15377                 buttons: this.OK,
15378                 fn: fn,
15379                 scope : scope,
15380                 modal : true
15381             });
15382             return this;
15383         },
15384
15385         /**
15386          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15387          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15388          * You are responsible for closing the message box when the process is complete.
15389          * @param {String} msg The message box body text
15390          * @param {String} title (optional) The title bar text
15391          * @return {Roo.MessageBox} This message box
15392          */
15393         wait : function(msg, title){
15394             this.show({
15395                 title : title,
15396                 msg : msg,
15397                 buttons: false,
15398                 closable:false,
15399                 progress:true,
15400                 modal:true,
15401                 width:300,
15402                 wait:true
15403             });
15404             waitTimer = Roo.TaskMgr.start({
15405                 run: function(i){
15406                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15407                 },
15408                 interval: 1000
15409             });
15410             return this;
15411         },
15412
15413         /**
15414          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15415          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15416          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15417          * @param {String} title The title bar text
15418          * @param {String} msg The message box body text
15419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15420          * @param {Object} scope (optional) The scope of the callback function
15421          * @return {Roo.MessageBox} This message box
15422          */
15423         confirm : function(title, msg, fn, scope){
15424             this.show({
15425                 title : title,
15426                 msg : msg,
15427                 buttons: this.YESNO,
15428                 fn: fn,
15429                 scope : scope,
15430                 modal : true
15431             });
15432             return this;
15433         },
15434
15435         /**
15436          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15437          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15438          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15439          * (could also be the top-right close button) and the text that was entered will be passed as the two
15440          * parameters to the callback.
15441          * @param {String} title The title bar text
15442          * @param {String} msg The message box body text
15443          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15444          * @param {Object} scope (optional) The scope of the callback function
15445          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15446          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15447          * @return {Roo.MessageBox} This message box
15448          */
15449         prompt : function(title, msg, fn, scope, multiline){
15450             this.show({
15451                 title : title,
15452                 msg : msg,
15453                 buttons: this.OKCANCEL,
15454                 fn: fn,
15455                 minWidth:250,
15456                 scope : scope,
15457                 prompt:true,
15458                 multiline: multiline,
15459                 modal : true
15460             });
15461             return this;
15462         },
15463
15464         /**
15465          * Button config that displays a single OK button
15466          * @type Object
15467          */
15468         OK : {ok:true},
15469         /**
15470          * Button config that displays Yes and No buttons
15471          * @type Object
15472          */
15473         YESNO : {yes:true, no:true},
15474         /**
15475          * Button config that displays OK and Cancel buttons
15476          * @type Object
15477          */
15478         OKCANCEL : {ok:true, cancel:true},
15479         /**
15480          * Button config that displays Yes, No and Cancel buttons
15481          * @type Object
15482          */
15483         YESNOCANCEL : {yes:true, no:true, cancel:true},
15484
15485         /**
15486          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15487          * @type Number
15488          */
15489         defaultTextHeight : 75,
15490         /**
15491          * The maximum width in pixels of the message box (defaults to 600)
15492          * @type Number
15493          */
15494         maxWidth : 600,
15495         /**
15496          * The minimum width in pixels of the message box (defaults to 100)
15497          * @type Number
15498          */
15499         minWidth : 100,
15500         /**
15501          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15502          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15503          * @type Number
15504          */
15505         minProgressWidth : 250,
15506         /**
15507          * An object containing the default button text strings that can be overriden for localized language support.
15508          * Supported properties are: ok, cancel, yes and no.
15509          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15510          * @type Object
15511          */
15512         buttonText : {
15513             ok : "OK",
15514             cancel : "Cancel",
15515             yes : "Yes",
15516             no : "No"
15517         }
15518     };
15519 }();
15520
15521 /**
15522  * Shorthand for {@link Roo.MessageBox}
15523  */
15524 Roo.Msg = Roo.MessageBox;/*
15525  * Based on:
15526  * Ext JS Library 1.1.1
15527  * Copyright(c) 2006-2007, Ext JS, LLC.
15528  *
15529  * Originally Released Under LGPL - original licence link has changed is not relivant.
15530  *
15531  * Fork - LGPL
15532  * <script type="text/javascript">
15533  */
15534 /**
15535  * @class Roo.QuickTips
15536  * Provides attractive and customizable tooltips for any element.
15537  * @singleton
15538  */
15539 Roo.QuickTips = function(){
15540     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15541     var ce, bd, xy, dd;
15542     var visible = false, disabled = true, inited = false;
15543     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15544     
15545     var onOver = function(e){
15546         if(disabled){
15547             return;
15548         }
15549         var t = e.getTarget();
15550         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15551             return;
15552         }
15553         if(ce && t == ce.el){
15554             clearTimeout(hideProc);
15555             return;
15556         }
15557         if(t && tagEls[t.id]){
15558             tagEls[t.id].el = t;
15559             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15560             return;
15561         }
15562         var ttp, et = Roo.fly(t);
15563         var ns = cfg.namespace;
15564         if(tm.interceptTitles && t.title){
15565             ttp = t.title;
15566             t.qtip = ttp;
15567             t.removeAttribute("title");
15568             e.preventDefault();
15569         }else{
15570             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15571         }
15572         if(ttp){
15573             showProc = show.defer(tm.showDelay, tm, [{
15574                 el: t, 
15575                 text: ttp, 
15576                 width: et.getAttributeNS(ns, cfg.width),
15577                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15578                 title: et.getAttributeNS(ns, cfg.title),
15579                     cls: et.getAttributeNS(ns, cfg.cls)
15580             }]);
15581         }
15582     };
15583     
15584     var onOut = function(e){
15585         clearTimeout(showProc);
15586         var t = e.getTarget();
15587         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15588             hideProc = setTimeout(hide, tm.hideDelay);
15589         }
15590     };
15591     
15592     var onMove = function(e){
15593         if(disabled){
15594             return;
15595         }
15596         xy = e.getXY();
15597         xy[1] += 18;
15598         if(tm.trackMouse && ce){
15599             el.setXY(xy);
15600         }
15601     };
15602     
15603     var onDown = function(e){
15604         clearTimeout(showProc);
15605         clearTimeout(hideProc);
15606         if(!e.within(el)){
15607             if(tm.hideOnClick){
15608                 hide();
15609                 tm.disable();
15610                 tm.enable.defer(100, tm);
15611             }
15612         }
15613     };
15614     
15615     var getPad = function(){
15616         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15617     };
15618
15619     var show = function(o){
15620         if(disabled){
15621             return;
15622         }
15623         clearTimeout(dismissProc);
15624         ce = o;
15625         if(removeCls){ // in case manually hidden
15626             el.removeClass(removeCls);
15627             removeCls = null;
15628         }
15629         if(ce.cls){
15630             el.addClass(ce.cls);
15631             removeCls = ce.cls;
15632         }
15633         if(ce.title){
15634             tipTitle.update(ce.title);
15635             tipTitle.show();
15636         }else{
15637             tipTitle.update('');
15638             tipTitle.hide();
15639         }
15640         el.dom.style.width  = tm.maxWidth+'px';
15641         //tipBody.dom.style.width = '';
15642         tipBodyText.update(o.text);
15643         var p = getPad(), w = ce.width;
15644         if(!w){
15645             var td = tipBodyText.dom;
15646             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15647             if(aw > tm.maxWidth){
15648                 w = tm.maxWidth;
15649             }else if(aw < tm.minWidth){
15650                 w = tm.minWidth;
15651             }else{
15652                 w = aw;
15653             }
15654         }
15655         //tipBody.setWidth(w);
15656         el.setWidth(parseInt(w, 10) + p);
15657         if(ce.autoHide === false){
15658             close.setDisplayed(true);
15659             if(dd){
15660                 dd.unlock();
15661             }
15662         }else{
15663             close.setDisplayed(false);
15664             if(dd){
15665                 dd.lock();
15666             }
15667         }
15668         if(xy){
15669             el.avoidY = xy[1]-18;
15670             el.setXY(xy);
15671         }
15672         if(tm.animate){
15673             el.setOpacity(.1);
15674             el.setStyle("visibility", "visible");
15675             el.fadeIn({callback: afterShow});
15676         }else{
15677             afterShow();
15678         }
15679     };
15680     
15681     var afterShow = function(){
15682         if(ce){
15683             el.show();
15684             esc.enable();
15685             if(tm.autoDismiss && ce.autoHide !== false){
15686                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15687             }
15688         }
15689     };
15690     
15691     var hide = function(noanim){
15692         clearTimeout(dismissProc);
15693         clearTimeout(hideProc);
15694         ce = null;
15695         if(el.isVisible()){
15696             esc.disable();
15697             if(noanim !== true && tm.animate){
15698                 el.fadeOut({callback: afterHide});
15699             }else{
15700                 afterHide();
15701             } 
15702         }
15703     };
15704     
15705     var afterHide = function(){
15706         el.hide();
15707         if(removeCls){
15708             el.removeClass(removeCls);
15709             removeCls = null;
15710         }
15711     };
15712     
15713     return {
15714         /**
15715         * @cfg {Number} minWidth
15716         * The minimum width of the quick tip (defaults to 40)
15717         */
15718        minWidth : 40,
15719         /**
15720         * @cfg {Number} maxWidth
15721         * The maximum width of the quick tip (defaults to 300)
15722         */
15723        maxWidth : 300,
15724         /**
15725         * @cfg {Boolean} interceptTitles
15726         * True to automatically use the element's DOM title value if available (defaults to false)
15727         */
15728        interceptTitles : false,
15729         /**
15730         * @cfg {Boolean} trackMouse
15731         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15732         */
15733        trackMouse : false,
15734         /**
15735         * @cfg {Boolean} hideOnClick
15736         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15737         */
15738        hideOnClick : true,
15739         /**
15740         * @cfg {Number} showDelay
15741         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15742         */
15743        showDelay : 500,
15744         /**
15745         * @cfg {Number} hideDelay
15746         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15747         */
15748        hideDelay : 200,
15749         /**
15750         * @cfg {Boolean} autoHide
15751         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15752         * Used in conjunction with hideDelay.
15753         */
15754        autoHide : true,
15755         /**
15756         * @cfg {Boolean}
15757         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15758         * (defaults to true).  Used in conjunction with autoDismissDelay.
15759         */
15760        autoDismiss : true,
15761         /**
15762         * @cfg {Number}
15763         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15764         */
15765        autoDismissDelay : 5000,
15766        /**
15767         * @cfg {Boolean} animate
15768         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15769         */
15770        animate : false,
15771
15772        /**
15773         * @cfg {String} title
15774         * Title text to display (defaults to '').  This can be any valid HTML markup.
15775         */
15776         title: '',
15777        /**
15778         * @cfg {String} text
15779         * Body text to display (defaults to '').  This can be any valid HTML markup.
15780         */
15781         text : '',
15782        /**
15783         * @cfg {String} cls
15784         * A CSS class to apply to the base quick tip element (defaults to '').
15785         */
15786         cls : '',
15787        /**
15788         * @cfg {Number} width
15789         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15790         * minWidth or maxWidth.
15791         */
15792         width : null,
15793
15794     /**
15795      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15796      * or display QuickTips in a page.
15797      */
15798        init : function(){
15799           tm = Roo.QuickTips;
15800           cfg = tm.tagConfig;
15801           if(!inited){
15802               if(!Roo.isReady){ // allow calling of init() before onReady
15803                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15804                   return;
15805               }
15806               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15807               el.fxDefaults = {stopFx: true};
15808               // maximum custom styling
15809               //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>');
15810               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>');              
15811               tipTitle = el.child('h3');
15812               tipTitle.enableDisplayMode("block");
15813               tipBody = el.child('div.x-tip-bd');
15814               tipBodyText = el.child('div.x-tip-bd-inner');
15815               //bdLeft = el.child('div.x-tip-bd-left');
15816               //bdRight = el.child('div.x-tip-bd-right');
15817               close = el.child('div.x-tip-close');
15818               close.enableDisplayMode("block");
15819               close.on("click", hide);
15820               var d = Roo.get(document);
15821               d.on("mousedown", onDown);
15822               d.on("mouseover", onOver);
15823               d.on("mouseout", onOut);
15824               d.on("mousemove", onMove);
15825               esc = d.addKeyListener(27, hide);
15826               esc.disable();
15827               if(Roo.dd.DD){
15828                   dd = el.initDD("default", null, {
15829                       onDrag : function(){
15830                           el.sync();  
15831                       }
15832                   });
15833                   dd.setHandleElId(tipTitle.id);
15834                   dd.lock();
15835               }
15836               inited = true;
15837           }
15838           this.enable(); 
15839        },
15840
15841     /**
15842      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15843      * are supported:
15844      * <pre>
15845 Property    Type                   Description
15846 ----------  ---------------------  ------------------------------------------------------------------------
15847 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15848      * </ul>
15849      * @param {Object} config The config object
15850      */
15851        register : function(config){
15852            var cs = config instanceof Array ? config : arguments;
15853            for(var i = 0, len = cs.length; i < len; i++) {
15854                var c = cs[i];
15855                var target = c.target;
15856                if(target){
15857                    if(target instanceof Array){
15858                        for(var j = 0, jlen = target.length; j < jlen; j++){
15859                            tagEls[target[j]] = c;
15860                        }
15861                    }else{
15862                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15863                    }
15864                }
15865            }
15866        },
15867
15868     /**
15869      * Removes this quick tip from its element and destroys it.
15870      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15871      */
15872        unregister : function(el){
15873            delete tagEls[Roo.id(el)];
15874        },
15875
15876     /**
15877      * Enable this quick tip.
15878      */
15879        enable : function(){
15880            if(inited && disabled){
15881                locks.pop();
15882                if(locks.length < 1){
15883                    disabled = false;
15884                }
15885            }
15886        },
15887
15888     /**
15889      * Disable this quick tip.
15890      */
15891        disable : function(){
15892           disabled = true;
15893           clearTimeout(showProc);
15894           clearTimeout(hideProc);
15895           clearTimeout(dismissProc);
15896           if(ce){
15897               hide(true);
15898           }
15899           locks.push(1);
15900        },
15901
15902     /**
15903      * Returns true if the quick tip is enabled, else false.
15904      */
15905        isEnabled : function(){
15906             return !disabled;
15907        },
15908
15909         // private
15910        tagConfig : {
15911            namespace : "ext",
15912            attribute : "qtip",
15913            width : "width",
15914            target : "target",
15915            title : "qtitle",
15916            hide : "hide",
15917            cls : "qclass"
15918        }
15919    };
15920 }();
15921
15922 // backwards compat
15923 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15924  * Based on:
15925  * Ext JS Library 1.1.1
15926  * Copyright(c) 2006-2007, Ext JS, LLC.
15927  *
15928  * Originally Released Under LGPL - original licence link has changed is not relivant.
15929  *
15930  * Fork - LGPL
15931  * <script type="text/javascript">
15932  */
15933  
15934
15935 /**
15936  * @class Roo.tree.TreePanel
15937  * @extends Roo.data.Tree
15938
15939  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15940  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15941  * @cfg {Boolean} enableDD true to enable drag and drop
15942  * @cfg {Boolean} enableDrag true to enable just drag
15943  * @cfg {Boolean} enableDrop true to enable just drop
15944  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15945  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15946  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15947  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15948  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15949  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15950  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15951  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15952  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15953  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15954  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15955  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15956  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15957  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15958  * @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>
15959  * @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>
15960  * 
15961  * @constructor
15962  * @param {String/HTMLElement/Element} el The container element
15963  * @param {Object} config
15964  */
15965 Roo.tree.TreePanel = function(el, config){
15966     var root = false;
15967     var loader = false;
15968     if (config.root) {
15969         root = config.root;
15970         delete config.root;
15971     }
15972     if (config.loader) {
15973         loader = config.loader;
15974         delete config.loader;
15975     }
15976     
15977     Roo.apply(this, config);
15978     Roo.tree.TreePanel.superclass.constructor.call(this);
15979     this.el = Roo.get(el);
15980     this.el.addClass('x-tree');
15981     //console.log(root);
15982     if (root) {
15983         this.setRootNode( Roo.factory(root, Roo.tree));
15984     }
15985     if (loader) {
15986         this.loader = Roo.factory(loader, Roo.tree);
15987     }
15988    /**
15989     * Read-only. The id of the container element becomes this TreePanel's id.
15990     */
15991     this.id = this.el.id;
15992     this.addEvents({
15993         /**
15994         * @event beforeload
15995         * Fires before a node is loaded, return false to cancel
15996         * @param {Node} node The node being loaded
15997         */
15998         "beforeload" : true,
15999         /**
16000         * @event load
16001         * Fires when a node is loaded
16002         * @param {Node} node The node that was loaded
16003         */
16004         "load" : true,
16005         /**
16006         * @event textchange
16007         * Fires when the text for a node is changed
16008         * @param {Node} node The node
16009         * @param {String} text The new text
16010         * @param {String} oldText The old text
16011         */
16012         "textchange" : true,
16013         /**
16014         * @event beforeexpand
16015         * Fires before a node is expanded, return false to cancel.
16016         * @param {Node} node The node
16017         * @param {Boolean} deep
16018         * @param {Boolean} anim
16019         */
16020         "beforeexpand" : true,
16021         /**
16022         * @event beforecollapse
16023         * Fires before a node is collapsed, return false to cancel.
16024         * @param {Node} node The node
16025         * @param {Boolean} deep
16026         * @param {Boolean} anim
16027         */
16028         "beforecollapse" : true,
16029         /**
16030         * @event expand
16031         * Fires when a node is expanded
16032         * @param {Node} node The node
16033         */
16034         "expand" : true,
16035         /**
16036         * @event disabledchange
16037         * Fires when the disabled status of a node changes
16038         * @param {Node} node The node
16039         * @param {Boolean} disabled
16040         */
16041         "disabledchange" : true,
16042         /**
16043         * @event collapse
16044         * Fires when a node is collapsed
16045         * @param {Node} node The node
16046         */
16047         "collapse" : true,
16048         /**
16049         * @event beforeclick
16050         * Fires before click processing on a node. Return false to cancel the default action.
16051         * @param {Node} node The node
16052         * @param {Roo.EventObject} e The event object
16053         */
16054         "beforeclick":true,
16055         /**
16056         * @event checkchange
16057         * Fires when a node with a checkbox's checked property changes
16058         * @param {Node} this This node
16059         * @param {Boolean} checked
16060         */
16061         "checkchange":true,
16062         /**
16063         * @event click
16064         * Fires when a node is clicked
16065         * @param {Node} node The node
16066         * @param {Roo.EventObject} e The event object
16067         */
16068         "click":true,
16069         /**
16070         * @event dblclick
16071         * Fires when a node is double clicked
16072         * @param {Node} node The node
16073         * @param {Roo.EventObject} e The event object
16074         */
16075         "dblclick":true,
16076         /**
16077         * @event contextmenu
16078         * Fires when a node is right clicked
16079         * @param {Node} node The node
16080         * @param {Roo.EventObject} e The event object
16081         */
16082         "contextmenu":true,
16083         /**
16084         * @event beforechildrenrendered
16085         * Fires right before the child nodes for a node are rendered
16086         * @param {Node} node The node
16087         */
16088         "beforechildrenrendered":true,
16089         /**
16090         * @event startdrag
16091         * Fires when a node starts being dragged
16092         * @param {Roo.tree.TreePanel} this
16093         * @param {Roo.tree.TreeNode} node
16094         * @param {event} e The raw browser event
16095         */ 
16096        "startdrag" : true,
16097        /**
16098         * @event enddrag
16099         * Fires when a drag operation is complete
16100         * @param {Roo.tree.TreePanel} this
16101         * @param {Roo.tree.TreeNode} node
16102         * @param {event} e The raw browser event
16103         */
16104        "enddrag" : true,
16105        /**
16106         * @event dragdrop
16107         * Fires when a dragged node is dropped on a valid DD target
16108         * @param {Roo.tree.TreePanel} this
16109         * @param {Roo.tree.TreeNode} node
16110         * @param {DD} dd The dd it was dropped on
16111         * @param {event} e The raw browser event
16112         */
16113        "dragdrop" : true,
16114        /**
16115         * @event beforenodedrop
16116         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16117         * passed to handlers has the following properties:<br />
16118         * <ul style="padding:5px;padding-left:16px;">
16119         * <li>tree - The TreePanel</li>
16120         * <li>target - The node being targeted for the drop</li>
16121         * <li>data - The drag data from the drag source</li>
16122         * <li>point - The point of the drop - append, above or below</li>
16123         * <li>source - The drag source</li>
16124         * <li>rawEvent - Raw mouse event</li>
16125         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16126         * to be inserted by setting them on this object.</li>
16127         * <li>cancel - Set this to true to cancel the drop.</li>
16128         * </ul>
16129         * @param {Object} dropEvent
16130         */
16131        "beforenodedrop" : true,
16132        /**
16133         * @event nodedrop
16134         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16135         * passed to handlers has the following properties:<br />
16136         * <ul style="padding:5px;padding-left:16px;">
16137         * <li>tree - The TreePanel</li>
16138         * <li>target - The node being targeted for the drop</li>
16139         * <li>data - The drag data from the drag source</li>
16140         * <li>point - The point of the drop - append, above or below</li>
16141         * <li>source - The drag source</li>
16142         * <li>rawEvent - Raw mouse event</li>
16143         * <li>dropNode - Dropped node(s).</li>
16144         * </ul>
16145         * @param {Object} dropEvent
16146         */
16147        "nodedrop" : true,
16148         /**
16149         * @event nodedragover
16150         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16151         * passed to handlers has the following properties:<br />
16152         * <ul style="padding:5px;padding-left:16px;">
16153         * <li>tree - The TreePanel</li>
16154         * <li>target - The node being targeted for the drop</li>
16155         * <li>data - The drag data from the drag source</li>
16156         * <li>point - The point of the drop - append, above or below</li>
16157         * <li>source - The drag source</li>
16158         * <li>rawEvent - Raw mouse event</li>
16159         * <li>dropNode - Drop node(s) provided by the source.</li>
16160         * <li>cancel - Set this to true to signal drop not allowed.</li>
16161         * </ul>
16162         * @param {Object} dragOverEvent
16163         */
16164        "nodedragover" : true
16165         
16166     });
16167     if(this.singleExpand){
16168        this.on("beforeexpand", this.restrictExpand, this);
16169     }
16170     if (this.editor) {
16171         this.editor.tree = this;
16172         this.editor = Roo.factory(this.editor, Roo.tree);
16173     }
16174     
16175     if (this.selModel) {
16176         this.selModel = Roo.factory(this.selModel, Roo.tree);
16177     }
16178    
16179 };
16180 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16181     rootVisible : true,
16182     animate: Roo.enableFx,
16183     lines : true,
16184     enableDD : false,
16185     hlDrop : Roo.enableFx,
16186   
16187     renderer: false,
16188     
16189     rendererTip: false,
16190     // private
16191     restrictExpand : function(node){
16192         var p = node.parentNode;
16193         if(p){
16194             if(p.expandedChild && p.expandedChild.parentNode == p){
16195                 p.expandedChild.collapse();
16196             }
16197             p.expandedChild = node;
16198         }
16199     },
16200
16201     // private override
16202     setRootNode : function(node){
16203         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16204         if(!this.rootVisible){
16205             node.ui = new Roo.tree.RootTreeNodeUI(node);
16206         }
16207         return node;
16208     },
16209
16210     /**
16211      * Returns the container element for this TreePanel
16212      */
16213     getEl : function(){
16214         return this.el;
16215     },
16216
16217     /**
16218      * Returns the default TreeLoader for this TreePanel
16219      */
16220     getLoader : function(){
16221         return this.loader;
16222     },
16223
16224     /**
16225      * Expand all nodes
16226      */
16227     expandAll : function(){
16228         this.root.expand(true);
16229     },
16230
16231     /**
16232      * Collapse all nodes
16233      */
16234     collapseAll : function(){
16235         this.root.collapse(true);
16236     },
16237
16238     /**
16239      * Returns the selection model used by this TreePanel
16240      */
16241     getSelectionModel : function(){
16242         if(!this.selModel){
16243             this.selModel = new Roo.tree.DefaultSelectionModel();
16244         }
16245         return this.selModel;
16246     },
16247
16248     /**
16249      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16250      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16251      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16252      * @return {Array}
16253      */
16254     getChecked : function(a, startNode){
16255         startNode = startNode || this.root;
16256         var r = [];
16257         var f = function(){
16258             if(this.attributes.checked){
16259                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16260             }
16261         }
16262         startNode.cascade(f);
16263         return r;
16264     },
16265
16266     /**
16267      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16268      * @param {String} path
16269      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16270      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16271      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16272      */
16273     expandPath : function(path, attr, callback){
16274         attr = attr || "id";
16275         var keys = path.split(this.pathSeparator);
16276         var curNode = this.root;
16277         if(curNode.attributes[attr] != keys[1]){ // invalid root
16278             if(callback){
16279                 callback(false, null);
16280             }
16281             return;
16282         }
16283         var index = 1;
16284         var f = function(){
16285             if(++index == keys.length){
16286                 if(callback){
16287                     callback(true, curNode);
16288                 }
16289                 return;
16290             }
16291             var c = curNode.findChild(attr, keys[index]);
16292             if(!c){
16293                 if(callback){
16294                     callback(false, curNode);
16295                 }
16296                 return;
16297             }
16298             curNode = c;
16299             c.expand(false, false, f);
16300         };
16301         curNode.expand(false, false, f);
16302     },
16303
16304     /**
16305      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16306      * @param {String} path
16307      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16308      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16309      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16310      */
16311     selectPath : function(path, attr, callback){
16312         attr = attr || "id";
16313         var keys = path.split(this.pathSeparator);
16314         var v = keys.pop();
16315         if(keys.length > 0){
16316             var f = function(success, node){
16317                 if(success && node){
16318                     var n = node.findChild(attr, v);
16319                     if(n){
16320                         n.select();
16321                         if(callback){
16322                             callback(true, n);
16323                         }
16324                     }else if(callback){
16325                         callback(false, n);
16326                     }
16327                 }else{
16328                     if(callback){
16329                         callback(false, n);
16330                     }
16331                 }
16332             };
16333             this.expandPath(keys.join(this.pathSeparator), attr, f);
16334         }else{
16335             this.root.select();
16336             if(callback){
16337                 callback(true, this.root);
16338             }
16339         }
16340     },
16341
16342     getTreeEl : function(){
16343         return this.el;
16344     },
16345
16346     /**
16347      * Trigger rendering of this TreePanel
16348      */
16349     render : function(){
16350         if (this.innerCt) {
16351             return this; // stop it rendering more than once!!
16352         }
16353         
16354         this.innerCt = this.el.createChild({tag:"ul",
16355                cls:"x-tree-root-ct " +
16356                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16357
16358         if(this.containerScroll){
16359             Roo.dd.ScrollManager.register(this.el);
16360         }
16361         if((this.enableDD || this.enableDrop) && !this.dropZone){
16362            /**
16363             * The dropZone used by this tree if drop is enabled
16364             * @type Roo.tree.TreeDropZone
16365             */
16366              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16367                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16368            });
16369         }
16370         if((this.enableDD || this.enableDrag) && !this.dragZone){
16371            /**
16372             * The dragZone used by this tree if drag is enabled
16373             * @type Roo.tree.TreeDragZone
16374             */
16375             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16376                ddGroup: this.ddGroup || "TreeDD",
16377                scroll: this.ddScroll
16378            });
16379         }
16380         this.getSelectionModel().init(this);
16381         if (!this.root) {
16382             Roo.log("ROOT not set in tree");
16383             return this;
16384         }
16385         this.root.render();
16386         if(!this.rootVisible){
16387             this.root.renderChildren();
16388         }
16389         return this;
16390     }
16391 });/*
16392  * Based on:
16393  * Ext JS Library 1.1.1
16394  * Copyright(c) 2006-2007, Ext JS, LLC.
16395  *
16396  * Originally Released Under LGPL - original licence link has changed is not relivant.
16397  *
16398  * Fork - LGPL
16399  * <script type="text/javascript">
16400  */
16401  
16402
16403 /**
16404  * @class Roo.tree.DefaultSelectionModel
16405  * @extends Roo.util.Observable
16406  * The default single selection for a TreePanel.
16407  * @param {Object} cfg Configuration
16408  */
16409 Roo.tree.DefaultSelectionModel = function(cfg){
16410    this.selNode = null;
16411    
16412    
16413    
16414    this.addEvents({
16415        /**
16416         * @event selectionchange
16417         * Fires when the selected node changes
16418         * @param {DefaultSelectionModel} this
16419         * @param {TreeNode} node the new selection
16420         */
16421        "selectionchange" : true,
16422
16423        /**
16424         * @event beforeselect
16425         * Fires before the selected node changes, return false to cancel the change
16426         * @param {DefaultSelectionModel} this
16427         * @param {TreeNode} node the new selection
16428         * @param {TreeNode} node the old selection
16429         */
16430        "beforeselect" : true
16431    });
16432    
16433     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16434 };
16435
16436 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16437     init : function(tree){
16438         this.tree = tree;
16439         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16440         tree.on("click", this.onNodeClick, this);
16441     },
16442     
16443     onNodeClick : function(node, e){
16444         if (e.ctrlKey && this.selNode == node)  {
16445             this.unselect(node);
16446             return;
16447         }
16448         this.select(node);
16449     },
16450     
16451     /**
16452      * Select a node.
16453      * @param {TreeNode} node The node to select
16454      * @return {TreeNode} The selected node
16455      */
16456     select : function(node){
16457         var last = this.selNode;
16458         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16459             if(last){
16460                 last.ui.onSelectedChange(false);
16461             }
16462             this.selNode = node;
16463             node.ui.onSelectedChange(true);
16464             this.fireEvent("selectionchange", this, node, last);
16465         }
16466         return node;
16467     },
16468     
16469     /**
16470      * Deselect a node.
16471      * @param {TreeNode} node The node to unselect
16472      */
16473     unselect : function(node){
16474         if(this.selNode == node){
16475             this.clearSelections();
16476         }    
16477     },
16478     
16479     /**
16480      * Clear all selections
16481      */
16482     clearSelections : function(){
16483         var n = this.selNode;
16484         if(n){
16485             n.ui.onSelectedChange(false);
16486             this.selNode = null;
16487             this.fireEvent("selectionchange", this, null);
16488         }
16489         return n;
16490     },
16491     
16492     /**
16493      * Get the selected node
16494      * @return {TreeNode} The selected node
16495      */
16496     getSelectedNode : function(){
16497         return this.selNode;    
16498     },
16499     
16500     /**
16501      * Returns true if the node is selected
16502      * @param {TreeNode} node The node to check
16503      * @return {Boolean}
16504      */
16505     isSelected : function(node){
16506         return this.selNode == node;  
16507     },
16508
16509     /**
16510      * Selects the node above the selected node in the tree, intelligently walking the nodes
16511      * @return TreeNode The new selection
16512      */
16513     selectPrevious : function(){
16514         var s = this.selNode || this.lastSelNode;
16515         if(!s){
16516             return null;
16517         }
16518         var ps = s.previousSibling;
16519         if(ps){
16520             if(!ps.isExpanded() || ps.childNodes.length < 1){
16521                 return this.select(ps);
16522             } else{
16523                 var lc = ps.lastChild;
16524                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16525                     lc = lc.lastChild;
16526                 }
16527                 return this.select(lc);
16528             }
16529         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16530             return this.select(s.parentNode);
16531         }
16532         return null;
16533     },
16534
16535     /**
16536      * Selects the node above the selected node in the tree, intelligently walking the nodes
16537      * @return TreeNode The new selection
16538      */
16539     selectNext : function(){
16540         var s = this.selNode || this.lastSelNode;
16541         if(!s){
16542             return null;
16543         }
16544         if(s.firstChild && s.isExpanded()){
16545              return this.select(s.firstChild);
16546          }else if(s.nextSibling){
16547              return this.select(s.nextSibling);
16548          }else if(s.parentNode){
16549             var newS = null;
16550             s.parentNode.bubble(function(){
16551                 if(this.nextSibling){
16552                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16553                     return false;
16554                 }
16555             });
16556             return newS;
16557          }
16558         return null;
16559     },
16560
16561     onKeyDown : function(e){
16562         var s = this.selNode || this.lastSelNode;
16563         // undesirable, but required
16564         var sm = this;
16565         if(!s){
16566             return;
16567         }
16568         var k = e.getKey();
16569         switch(k){
16570              case e.DOWN:
16571                  e.stopEvent();
16572                  this.selectNext();
16573              break;
16574              case e.UP:
16575                  e.stopEvent();
16576                  this.selectPrevious();
16577              break;
16578              case e.RIGHT:
16579                  e.preventDefault();
16580                  if(s.hasChildNodes()){
16581                      if(!s.isExpanded()){
16582                          s.expand();
16583                      }else if(s.firstChild){
16584                          this.select(s.firstChild, e);
16585                      }
16586                  }
16587              break;
16588              case e.LEFT:
16589                  e.preventDefault();
16590                  if(s.hasChildNodes() && s.isExpanded()){
16591                      s.collapse();
16592                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16593                      this.select(s.parentNode, e);
16594                  }
16595              break;
16596         };
16597     }
16598 });
16599
16600 /**
16601  * @class Roo.tree.MultiSelectionModel
16602  * @extends Roo.util.Observable
16603  * Multi selection for a TreePanel.
16604  * @param {Object} cfg Configuration
16605  */
16606 Roo.tree.MultiSelectionModel = function(){
16607    this.selNodes = [];
16608    this.selMap = {};
16609    this.addEvents({
16610        /**
16611         * @event selectionchange
16612         * Fires when the selected nodes change
16613         * @param {MultiSelectionModel} this
16614         * @param {Array} nodes Array of the selected nodes
16615         */
16616        "selectionchange" : true
16617    });
16618    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16619    
16620 };
16621
16622 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16623     init : function(tree){
16624         this.tree = tree;
16625         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16626         tree.on("click", this.onNodeClick, this);
16627     },
16628     
16629     onNodeClick : function(node, e){
16630         this.select(node, e, e.ctrlKey);
16631     },
16632     
16633     /**
16634      * Select a node.
16635      * @param {TreeNode} node The node to select
16636      * @param {EventObject} e (optional) An event associated with the selection
16637      * @param {Boolean} keepExisting True to retain existing selections
16638      * @return {TreeNode} The selected node
16639      */
16640     select : function(node, e, keepExisting){
16641         if(keepExisting !== true){
16642             this.clearSelections(true);
16643         }
16644         if(this.isSelected(node)){
16645             this.lastSelNode = node;
16646             return node;
16647         }
16648         this.selNodes.push(node);
16649         this.selMap[node.id] = node;
16650         this.lastSelNode = node;
16651         node.ui.onSelectedChange(true);
16652         this.fireEvent("selectionchange", this, this.selNodes);
16653         return node;
16654     },
16655     
16656     /**
16657      * Deselect a node.
16658      * @param {TreeNode} node The node to unselect
16659      */
16660     unselect : function(node){
16661         if(this.selMap[node.id]){
16662             node.ui.onSelectedChange(false);
16663             var sn = this.selNodes;
16664             var index = -1;
16665             if(sn.indexOf){
16666                 index = sn.indexOf(node);
16667             }else{
16668                 for(var i = 0, len = sn.length; i < len; i++){
16669                     if(sn[i] == node){
16670                         index = i;
16671                         break;
16672                     }
16673                 }
16674             }
16675             if(index != -1){
16676                 this.selNodes.splice(index, 1);
16677             }
16678             delete this.selMap[node.id];
16679             this.fireEvent("selectionchange", this, this.selNodes);
16680         }
16681     },
16682     
16683     /**
16684      * Clear all selections
16685      */
16686     clearSelections : function(suppressEvent){
16687         var sn = this.selNodes;
16688         if(sn.length > 0){
16689             for(var i = 0, len = sn.length; i < len; i++){
16690                 sn[i].ui.onSelectedChange(false);
16691             }
16692             this.selNodes = [];
16693             this.selMap = {};
16694             if(suppressEvent !== true){
16695                 this.fireEvent("selectionchange", this, this.selNodes);
16696             }
16697         }
16698     },
16699     
16700     /**
16701      * Returns true if the node is selected
16702      * @param {TreeNode} node The node to check
16703      * @return {Boolean}
16704      */
16705     isSelected : function(node){
16706         return this.selMap[node.id] ? true : false;  
16707     },
16708     
16709     /**
16710      * Returns an array of the selected nodes
16711      * @return {Array}
16712      */
16713     getSelectedNodes : function(){
16714         return this.selNodes;    
16715     },
16716
16717     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16718
16719     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16720
16721     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16722 });/*
16723  * Based on:
16724  * Ext JS Library 1.1.1
16725  * Copyright(c) 2006-2007, Ext JS, LLC.
16726  *
16727  * Originally Released Under LGPL - original licence link has changed is not relivant.
16728  *
16729  * Fork - LGPL
16730  * <script type="text/javascript">
16731  */
16732  
16733 /**
16734  * @class Roo.tree.TreeNode
16735  * @extends Roo.data.Node
16736  * @cfg {String} text The text for this node
16737  * @cfg {Boolean} expanded true to start the node expanded
16738  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16739  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16740  * @cfg {Boolean} disabled true to start the node disabled
16741  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16742  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16743  * @cfg {String} cls A css class to be added to the node
16744  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16745  * @cfg {String} href URL of the link used for the node (defaults to #)
16746  * @cfg {String} hrefTarget target frame for the link
16747  * @cfg {String} qtip An Ext QuickTip for the node
16748  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16749  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16750  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16751  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16752  * (defaults to undefined with no checkbox rendered)
16753  * @constructor
16754  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16755  */
16756 Roo.tree.TreeNode = function(attributes){
16757     attributes = attributes || {};
16758     if(typeof attributes == "string"){
16759         attributes = {text: attributes};
16760     }
16761     this.childrenRendered = false;
16762     this.rendered = false;
16763     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16764     this.expanded = attributes.expanded === true;
16765     this.isTarget = attributes.isTarget !== false;
16766     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16767     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16768
16769     /**
16770      * Read-only. The text for this node. To change it use setText().
16771      * @type String
16772      */
16773     this.text = attributes.text;
16774     /**
16775      * True if this node is disabled.
16776      * @type Boolean
16777      */
16778     this.disabled = attributes.disabled === true;
16779
16780     this.addEvents({
16781         /**
16782         * @event textchange
16783         * Fires when the text for this node is changed
16784         * @param {Node} this This node
16785         * @param {String} text The new text
16786         * @param {String} oldText The old text
16787         */
16788         "textchange" : true,
16789         /**
16790         * @event beforeexpand
16791         * Fires before this node is expanded, return false to cancel.
16792         * @param {Node} this This node
16793         * @param {Boolean} deep
16794         * @param {Boolean} anim
16795         */
16796         "beforeexpand" : true,
16797         /**
16798         * @event beforecollapse
16799         * Fires before this node is collapsed, return false to cancel.
16800         * @param {Node} this This node
16801         * @param {Boolean} deep
16802         * @param {Boolean} anim
16803         */
16804         "beforecollapse" : true,
16805         /**
16806         * @event expand
16807         * Fires when this node is expanded
16808         * @param {Node} this This node
16809         */
16810         "expand" : true,
16811         /**
16812         * @event disabledchange
16813         * Fires when the disabled status of this node changes
16814         * @param {Node} this This node
16815         * @param {Boolean} disabled
16816         */
16817         "disabledchange" : true,
16818         /**
16819         * @event collapse
16820         * Fires when this node is collapsed
16821         * @param {Node} this This node
16822         */
16823         "collapse" : true,
16824         /**
16825         * @event beforeclick
16826         * Fires before click processing. Return false to cancel the default action.
16827         * @param {Node} this This node
16828         * @param {Roo.EventObject} e The event object
16829         */
16830         "beforeclick":true,
16831         /**
16832         * @event checkchange
16833         * Fires when a node with a checkbox's checked property changes
16834         * @param {Node} this This node
16835         * @param {Boolean} checked
16836         */
16837         "checkchange":true,
16838         /**
16839         * @event click
16840         * Fires when this node is clicked
16841         * @param {Node} this This node
16842         * @param {Roo.EventObject} e The event object
16843         */
16844         "click":true,
16845         /**
16846         * @event dblclick
16847         * Fires when this node is double clicked
16848         * @param {Node} this This node
16849         * @param {Roo.EventObject} e The event object
16850         */
16851         "dblclick":true,
16852         /**
16853         * @event contextmenu
16854         * Fires when this node is right clicked
16855         * @param {Node} this This node
16856         * @param {Roo.EventObject} e The event object
16857         */
16858         "contextmenu":true,
16859         /**
16860         * @event beforechildrenrendered
16861         * Fires right before the child nodes for this node are rendered
16862         * @param {Node} this This node
16863         */
16864         "beforechildrenrendered":true
16865     });
16866
16867     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16868
16869     /**
16870      * Read-only. The UI for this node
16871      * @type TreeNodeUI
16872      */
16873     this.ui = new uiClass(this);
16874     
16875     // finally support items[]
16876     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16877         return;
16878     }
16879     
16880     
16881     Roo.each(this.attributes.items, function(c) {
16882         this.appendChild(Roo.factory(c,Roo.Tree));
16883     }, this);
16884     delete this.attributes.items;
16885     
16886     
16887     
16888 };
16889 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16890     preventHScroll: true,
16891     /**
16892      * Returns true if this node is expanded
16893      * @return {Boolean}
16894      */
16895     isExpanded : function(){
16896         return this.expanded;
16897     },
16898
16899     /**
16900      * Returns the UI object for this node
16901      * @return {TreeNodeUI}
16902      */
16903     getUI : function(){
16904         return this.ui;
16905     },
16906
16907     // private override
16908     setFirstChild : function(node){
16909         var of = this.firstChild;
16910         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16911         if(this.childrenRendered && of && node != of){
16912             of.renderIndent(true, true);
16913         }
16914         if(this.rendered){
16915             this.renderIndent(true, true);
16916         }
16917     },
16918
16919     // private override
16920     setLastChild : function(node){
16921         var ol = this.lastChild;
16922         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16923         if(this.childrenRendered && ol && node != ol){
16924             ol.renderIndent(true, true);
16925         }
16926         if(this.rendered){
16927             this.renderIndent(true, true);
16928         }
16929     },
16930
16931     // these methods are overridden to provide lazy rendering support
16932     // private override
16933     appendChild : function()
16934     {
16935         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16936         if(node && this.childrenRendered){
16937             node.render();
16938         }
16939         this.ui.updateExpandIcon();
16940         return node;
16941     },
16942
16943     // private override
16944     removeChild : function(node){
16945         this.ownerTree.getSelectionModel().unselect(node);
16946         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16947         // if it's been rendered remove dom node
16948         if(this.childrenRendered){
16949             node.ui.remove();
16950         }
16951         if(this.childNodes.length < 1){
16952             this.collapse(false, false);
16953         }else{
16954             this.ui.updateExpandIcon();
16955         }
16956         if(!this.firstChild) {
16957             this.childrenRendered = false;
16958         }
16959         return node;
16960     },
16961
16962     // private override
16963     insertBefore : function(node, refNode){
16964         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16965         if(newNode && refNode && this.childrenRendered){
16966             node.render();
16967         }
16968         this.ui.updateExpandIcon();
16969         return newNode;
16970     },
16971
16972     /**
16973      * Sets the text for this node
16974      * @param {String} text
16975      */
16976     setText : function(text){
16977         var oldText = this.text;
16978         this.text = text;
16979         this.attributes.text = text;
16980         if(this.rendered){ // event without subscribing
16981             this.ui.onTextChange(this, text, oldText);
16982         }
16983         this.fireEvent("textchange", this, text, oldText);
16984     },
16985
16986     /**
16987      * Triggers selection of this node
16988      */
16989     select : function(){
16990         this.getOwnerTree().getSelectionModel().select(this);
16991     },
16992
16993     /**
16994      * Triggers deselection of this node
16995      */
16996     unselect : function(){
16997         this.getOwnerTree().getSelectionModel().unselect(this);
16998     },
16999
17000     /**
17001      * Returns true if this node is selected
17002      * @return {Boolean}
17003      */
17004     isSelected : function(){
17005         return this.getOwnerTree().getSelectionModel().isSelected(this);
17006     },
17007
17008     /**
17009      * Expand this node.
17010      * @param {Boolean} deep (optional) True to expand all children as well
17011      * @param {Boolean} anim (optional) false to cancel the default animation
17012      * @param {Function} callback (optional) A callback to be called when
17013      * expanding this node completes (does not wait for deep expand to complete).
17014      * Called with 1 parameter, this node.
17015      */
17016     expand : function(deep, anim, callback){
17017         if(!this.expanded){
17018             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17019                 return;
17020             }
17021             if(!this.childrenRendered){
17022                 this.renderChildren();
17023             }
17024             this.expanded = true;
17025             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17026                 this.ui.animExpand(function(){
17027                     this.fireEvent("expand", this);
17028                     if(typeof callback == "function"){
17029                         callback(this);
17030                     }
17031                     if(deep === true){
17032                         this.expandChildNodes(true);
17033                     }
17034                 }.createDelegate(this));
17035                 return;
17036             }else{
17037                 this.ui.expand();
17038                 this.fireEvent("expand", this);
17039                 if(typeof callback == "function"){
17040                     callback(this);
17041                 }
17042             }
17043         }else{
17044            if(typeof callback == "function"){
17045                callback(this);
17046            }
17047         }
17048         if(deep === true){
17049             this.expandChildNodes(true);
17050         }
17051     },
17052
17053     isHiddenRoot : function(){
17054         return this.isRoot && !this.getOwnerTree().rootVisible;
17055     },
17056
17057     /**
17058      * Collapse this node.
17059      * @param {Boolean} deep (optional) True to collapse all children as well
17060      * @param {Boolean} anim (optional) false to cancel the default animation
17061      */
17062     collapse : function(deep, anim){
17063         if(this.expanded && !this.isHiddenRoot()){
17064             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17065                 return;
17066             }
17067             this.expanded = false;
17068             if((this.getOwnerTree().animate && anim !== false) || anim){
17069                 this.ui.animCollapse(function(){
17070                     this.fireEvent("collapse", this);
17071                     if(deep === true){
17072                         this.collapseChildNodes(true);
17073                     }
17074                 }.createDelegate(this));
17075                 return;
17076             }else{
17077                 this.ui.collapse();
17078                 this.fireEvent("collapse", this);
17079             }
17080         }
17081         if(deep === true){
17082             var cs = this.childNodes;
17083             for(var i = 0, len = cs.length; i < len; i++) {
17084                 cs[i].collapse(true, false);
17085             }
17086         }
17087     },
17088
17089     // private
17090     delayedExpand : function(delay){
17091         if(!this.expandProcId){
17092             this.expandProcId = this.expand.defer(delay, this);
17093         }
17094     },
17095
17096     // private
17097     cancelExpand : function(){
17098         if(this.expandProcId){
17099             clearTimeout(this.expandProcId);
17100         }
17101         this.expandProcId = false;
17102     },
17103
17104     /**
17105      * Toggles expanded/collapsed state of the node
17106      */
17107     toggle : function(){
17108         if(this.expanded){
17109             this.collapse();
17110         }else{
17111             this.expand();
17112         }
17113     },
17114
17115     /**
17116      * Ensures all parent nodes are expanded
17117      */
17118     ensureVisible : function(callback){
17119         var tree = this.getOwnerTree();
17120         tree.expandPath(this.parentNode.getPath(), false, function(){
17121             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17122             Roo.callback(callback);
17123         }.createDelegate(this));
17124     },
17125
17126     /**
17127      * Expand all child nodes
17128      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17129      */
17130     expandChildNodes : function(deep){
17131         var cs = this.childNodes;
17132         for(var i = 0, len = cs.length; i < len; i++) {
17133                 cs[i].expand(deep);
17134         }
17135     },
17136
17137     /**
17138      * Collapse all child nodes
17139      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17140      */
17141     collapseChildNodes : function(deep){
17142         var cs = this.childNodes;
17143         for(var i = 0, len = cs.length; i < len; i++) {
17144                 cs[i].collapse(deep);
17145         }
17146     },
17147
17148     /**
17149      * Disables this node
17150      */
17151     disable : function(){
17152         this.disabled = true;
17153         this.unselect();
17154         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17155             this.ui.onDisableChange(this, true);
17156         }
17157         this.fireEvent("disabledchange", this, true);
17158     },
17159
17160     /**
17161      * Enables this node
17162      */
17163     enable : function(){
17164         this.disabled = false;
17165         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17166             this.ui.onDisableChange(this, false);
17167         }
17168         this.fireEvent("disabledchange", this, false);
17169     },
17170
17171     // private
17172     renderChildren : function(suppressEvent){
17173         if(suppressEvent !== false){
17174             this.fireEvent("beforechildrenrendered", this);
17175         }
17176         var cs = this.childNodes;
17177         for(var i = 0, len = cs.length; i < len; i++){
17178             cs[i].render(true);
17179         }
17180         this.childrenRendered = true;
17181     },
17182
17183     // private
17184     sort : function(fn, scope){
17185         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17186         if(this.childrenRendered){
17187             var cs = this.childNodes;
17188             for(var i = 0, len = cs.length; i < len; i++){
17189                 cs[i].render(true);
17190             }
17191         }
17192     },
17193
17194     // private
17195     render : function(bulkRender){
17196         this.ui.render(bulkRender);
17197         if(!this.rendered){
17198             this.rendered = true;
17199             if(this.expanded){
17200                 this.expanded = false;
17201                 this.expand(false, false);
17202             }
17203         }
17204     },
17205
17206     // private
17207     renderIndent : function(deep, refresh){
17208         if(refresh){
17209             this.ui.childIndent = null;
17210         }
17211         this.ui.renderIndent();
17212         if(deep === true && this.childrenRendered){
17213             var cs = this.childNodes;
17214             for(var i = 0, len = cs.length; i < len; i++){
17215                 cs[i].renderIndent(true, refresh);
17216             }
17217         }
17218     }
17219 });/*
17220  * Based on:
17221  * Ext JS Library 1.1.1
17222  * Copyright(c) 2006-2007, Ext JS, LLC.
17223  *
17224  * Originally Released Under LGPL - original licence link has changed is not relivant.
17225  *
17226  * Fork - LGPL
17227  * <script type="text/javascript">
17228  */
17229  
17230 /**
17231  * @class Roo.tree.AsyncTreeNode
17232  * @extends Roo.tree.TreeNode
17233  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17234  * @constructor
17235  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17236  */
17237  Roo.tree.AsyncTreeNode = function(config){
17238     this.loaded = false;
17239     this.loading = false;
17240     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17241     /**
17242     * @event beforeload
17243     * Fires before this node is loaded, return false to cancel
17244     * @param {Node} this This node
17245     */
17246     this.addEvents({'beforeload':true, 'load': true});
17247     /**
17248     * @event load
17249     * Fires when this node is loaded
17250     * @param {Node} this This node
17251     */
17252     /**
17253      * The loader used by this node (defaults to using the tree's defined loader)
17254      * @type TreeLoader
17255      * @property loader
17256      */
17257 };
17258 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17259     expand : function(deep, anim, callback){
17260         if(this.loading){ // if an async load is already running, waiting til it's done
17261             var timer;
17262             var f = function(){
17263                 if(!this.loading){ // done loading
17264                     clearInterval(timer);
17265                     this.expand(deep, anim, callback);
17266                 }
17267             }.createDelegate(this);
17268             timer = setInterval(f, 200);
17269             return;
17270         }
17271         if(!this.loaded){
17272             if(this.fireEvent("beforeload", this) === false){
17273                 return;
17274             }
17275             this.loading = true;
17276             this.ui.beforeLoad(this);
17277             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17278             if(loader){
17279                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17280                 return;
17281             }
17282         }
17283         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17284     },
17285     
17286     /**
17287      * Returns true if this node is currently loading
17288      * @return {Boolean}
17289      */
17290     isLoading : function(){
17291         return this.loading;  
17292     },
17293     
17294     loadComplete : function(deep, anim, callback){
17295         this.loading = false;
17296         this.loaded = true;
17297         this.ui.afterLoad(this);
17298         this.fireEvent("load", this);
17299         this.expand(deep, anim, callback);
17300     },
17301     
17302     /**
17303      * Returns true if this node has been loaded
17304      * @return {Boolean}
17305      */
17306     isLoaded : function(){
17307         return this.loaded;
17308     },
17309     
17310     hasChildNodes : function(){
17311         if(!this.isLeaf() && !this.loaded){
17312             return true;
17313         }else{
17314             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17315         }
17316     },
17317
17318     /**
17319      * Trigger a reload for this node
17320      * @param {Function} callback
17321      */
17322     reload : function(callback){
17323         this.collapse(false, false);
17324         while(this.firstChild){
17325             this.removeChild(this.firstChild);
17326         }
17327         this.childrenRendered = false;
17328         this.loaded = false;
17329         if(this.isHiddenRoot()){
17330             this.expanded = false;
17331         }
17332         this.expand(false, false, callback);
17333     }
17334 });/*
17335  * Based on:
17336  * Ext JS Library 1.1.1
17337  * Copyright(c) 2006-2007, Ext JS, LLC.
17338  *
17339  * Originally Released Under LGPL - original licence link has changed is not relivant.
17340  *
17341  * Fork - LGPL
17342  * <script type="text/javascript">
17343  */
17344  
17345 /**
17346  * @class Roo.tree.TreeNodeUI
17347  * @constructor
17348  * @param {Object} node The node to render
17349  * The TreeNode UI implementation is separate from the
17350  * tree implementation. Unless you are customizing the tree UI,
17351  * you should never have to use this directly.
17352  */
17353 Roo.tree.TreeNodeUI = function(node){
17354     this.node = node;
17355     this.rendered = false;
17356     this.animating = false;
17357     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17358 };
17359
17360 Roo.tree.TreeNodeUI.prototype = {
17361     removeChild : function(node){
17362         if(this.rendered){
17363             this.ctNode.removeChild(node.ui.getEl());
17364         }
17365     },
17366
17367     beforeLoad : function(){
17368          this.addClass("x-tree-node-loading");
17369     },
17370
17371     afterLoad : function(){
17372          this.removeClass("x-tree-node-loading");
17373     },
17374
17375     onTextChange : function(node, text, oldText){
17376         if(this.rendered){
17377             this.textNode.innerHTML = text;
17378         }
17379     },
17380
17381     onDisableChange : function(node, state){
17382         this.disabled = state;
17383         if(state){
17384             this.addClass("x-tree-node-disabled");
17385         }else{
17386             this.removeClass("x-tree-node-disabled");
17387         }
17388     },
17389
17390     onSelectedChange : function(state){
17391         if(state){
17392             this.focus();
17393             this.addClass("x-tree-selected");
17394         }else{
17395             //this.blur();
17396             this.removeClass("x-tree-selected");
17397         }
17398     },
17399
17400     onMove : function(tree, node, oldParent, newParent, index, refNode){
17401         this.childIndent = null;
17402         if(this.rendered){
17403             var targetNode = newParent.ui.getContainer();
17404             if(!targetNode){//target not rendered
17405                 this.holder = document.createElement("div");
17406                 this.holder.appendChild(this.wrap);
17407                 return;
17408             }
17409             var insertBefore = refNode ? refNode.ui.getEl() : null;
17410             if(insertBefore){
17411                 targetNode.insertBefore(this.wrap, insertBefore);
17412             }else{
17413                 targetNode.appendChild(this.wrap);
17414             }
17415             this.node.renderIndent(true);
17416         }
17417     },
17418
17419     addClass : function(cls){
17420         if(this.elNode){
17421             Roo.fly(this.elNode).addClass(cls);
17422         }
17423     },
17424
17425     removeClass : function(cls){
17426         if(this.elNode){
17427             Roo.fly(this.elNode).removeClass(cls);
17428         }
17429     },
17430
17431     remove : function(){
17432         if(this.rendered){
17433             this.holder = document.createElement("div");
17434             this.holder.appendChild(this.wrap);
17435         }
17436     },
17437
17438     fireEvent : function(){
17439         return this.node.fireEvent.apply(this.node, arguments);
17440     },
17441
17442     initEvents : function(){
17443         this.node.on("move", this.onMove, this);
17444         var E = Roo.EventManager;
17445         var a = this.anchor;
17446
17447         var el = Roo.fly(a, '_treeui');
17448
17449         if(Roo.isOpera){ // opera render bug ignores the CSS
17450             el.setStyle("text-decoration", "none");
17451         }
17452
17453         el.on("click", this.onClick, this);
17454         el.on("dblclick", this.onDblClick, this);
17455
17456         if(this.checkbox){
17457             Roo.EventManager.on(this.checkbox,
17458                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17459         }
17460
17461         el.on("contextmenu", this.onContextMenu, this);
17462
17463         var icon = Roo.fly(this.iconNode);
17464         icon.on("click", this.onClick, this);
17465         icon.on("dblclick", this.onDblClick, this);
17466         icon.on("contextmenu", this.onContextMenu, this);
17467         E.on(this.ecNode, "click", this.ecClick, this, true);
17468
17469         if(this.node.disabled){
17470             this.addClass("x-tree-node-disabled");
17471         }
17472         if(this.node.hidden){
17473             this.addClass("x-tree-node-disabled");
17474         }
17475         var ot = this.node.getOwnerTree();
17476         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17477         if(dd && (!this.node.isRoot || ot.rootVisible)){
17478             Roo.dd.Registry.register(this.elNode, {
17479                 node: this.node,
17480                 handles: this.getDDHandles(),
17481                 isHandle: false
17482             });
17483         }
17484     },
17485
17486     getDDHandles : function(){
17487         return [this.iconNode, this.textNode];
17488     },
17489
17490     hide : function(){
17491         if(this.rendered){
17492             this.wrap.style.display = "none";
17493         }
17494     },
17495
17496     show : function(){
17497         if(this.rendered){
17498             this.wrap.style.display = "";
17499         }
17500     },
17501
17502     onContextMenu : function(e){
17503         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17504             e.preventDefault();
17505             this.focus();
17506             this.fireEvent("contextmenu", this.node, e);
17507         }
17508     },
17509
17510     onClick : function(e){
17511         if(this.dropping){
17512             e.stopEvent();
17513             return;
17514         }
17515         if(this.fireEvent("beforeclick", this.node, e) !== false){
17516             if(!this.disabled && this.node.attributes.href){
17517                 this.fireEvent("click", this.node, e);
17518                 return;
17519             }
17520             e.preventDefault();
17521             if(this.disabled){
17522                 return;
17523             }
17524
17525             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17526                 this.node.toggle();
17527             }
17528
17529             this.fireEvent("click", this.node, e);
17530         }else{
17531             e.stopEvent();
17532         }
17533     },
17534
17535     onDblClick : function(e){
17536         e.preventDefault();
17537         if(this.disabled){
17538             return;
17539         }
17540         if(this.checkbox){
17541             this.toggleCheck();
17542         }
17543         if(!this.animating && this.node.hasChildNodes()){
17544             this.node.toggle();
17545         }
17546         this.fireEvent("dblclick", this.node, e);
17547     },
17548
17549     onCheckChange : function(){
17550         var checked = this.checkbox.checked;
17551         this.node.attributes.checked = checked;
17552         this.fireEvent('checkchange', this.node, checked);
17553     },
17554
17555     ecClick : function(e){
17556         if(!this.animating && this.node.hasChildNodes()){
17557             this.node.toggle();
17558         }
17559     },
17560
17561     startDrop : function(){
17562         this.dropping = true;
17563     },
17564
17565     // delayed drop so the click event doesn't get fired on a drop
17566     endDrop : function(){
17567        setTimeout(function(){
17568            this.dropping = false;
17569        }.createDelegate(this), 50);
17570     },
17571
17572     expand : function(){
17573         this.updateExpandIcon();
17574         this.ctNode.style.display = "";
17575     },
17576
17577     focus : function(){
17578         if(!this.node.preventHScroll){
17579             try{this.anchor.focus();
17580             }catch(e){}
17581         }else if(!Roo.isIE){
17582             try{
17583                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17584                 var l = noscroll.scrollLeft;
17585                 this.anchor.focus();
17586                 noscroll.scrollLeft = l;
17587             }catch(e){}
17588         }
17589     },
17590
17591     toggleCheck : function(value){
17592         var cb = this.checkbox;
17593         if(cb){
17594             cb.checked = (value === undefined ? !cb.checked : value);
17595         }
17596     },
17597
17598     blur : function(){
17599         try{
17600             this.anchor.blur();
17601         }catch(e){}
17602     },
17603
17604     animExpand : function(callback){
17605         var ct = Roo.get(this.ctNode);
17606         ct.stopFx();
17607         if(!this.node.hasChildNodes()){
17608             this.updateExpandIcon();
17609             this.ctNode.style.display = "";
17610             Roo.callback(callback);
17611             return;
17612         }
17613         this.animating = true;
17614         this.updateExpandIcon();
17615
17616         ct.slideIn('t', {
17617            callback : function(){
17618                this.animating = false;
17619                Roo.callback(callback);
17620             },
17621             scope: this,
17622             duration: this.node.ownerTree.duration || .25
17623         });
17624     },
17625
17626     highlight : function(){
17627         var tree = this.node.getOwnerTree();
17628         Roo.fly(this.wrap).highlight(
17629             tree.hlColor || "C3DAF9",
17630             {endColor: tree.hlBaseColor}
17631         );
17632     },
17633
17634     collapse : function(){
17635         this.updateExpandIcon();
17636         this.ctNode.style.display = "none";
17637     },
17638
17639     animCollapse : function(callback){
17640         var ct = Roo.get(this.ctNode);
17641         ct.enableDisplayMode('block');
17642         ct.stopFx();
17643
17644         this.animating = true;
17645         this.updateExpandIcon();
17646
17647         ct.slideOut('t', {
17648             callback : function(){
17649                this.animating = false;
17650                Roo.callback(callback);
17651             },
17652             scope: this,
17653             duration: this.node.ownerTree.duration || .25
17654         });
17655     },
17656
17657     getContainer : function(){
17658         return this.ctNode;
17659     },
17660
17661     getEl : function(){
17662         return this.wrap;
17663     },
17664
17665     appendDDGhost : function(ghostNode){
17666         ghostNode.appendChild(this.elNode.cloneNode(true));
17667     },
17668
17669     getDDRepairXY : function(){
17670         return Roo.lib.Dom.getXY(this.iconNode);
17671     },
17672
17673     onRender : function(){
17674         this.render();
17675     },
17676
17677     render : function(bulkRender){
17678         var n = this.node, a = n.attributes;
17679         var targetNode = n.parentNode ?
17680               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17681
17682         if(!this.rendered){
17683             this.rendered = true;
17684
17685             this.renderElements(n, a, targetNode, bulkRender);
17686
17687             if(a.qtip){
17688                if(this.textNode.setAttributeNS){
17689                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17690                    if(a.qtipTitle){
17691                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17692                    }
17693                }else{
17694                    this.textNode.setAttribute("ext:qtip", a.qtip);
17695                    if(a.qtipTitle){
17696                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17697                    }
17698                }
17699             }else if(a.qtipCfg){
17700                 a.qtipCfg.target = Roo.id(this.textNode);
17701                 Roo.QuickTips.register(a.qtipCfg);
17702             }
17703             this.initEvents();
17704             if(!this.node.expanded){
17705                 this.updateExpandIcon();
17706             }
17707         }else{
17708             if(bulkRender === true) {
17709                 targetNode.appendChild(this.wrap);
17710             }
17711         }
17712     },
17713
17714     renderElements : function(n, a, targetNode, bulkRender)
17715     {
17716         // add some indent caching, this helps performance when rendering a large tree
17717         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17718         var t = n.getOwnerTree();
17719         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17720         if (typeof(n.attributes.html) != 'undefined') {
17721             txt = n.attributes.html;
17722         }
17723         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17724         var cb = typeof a.checked == 'boolean';
17725         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17726         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17727             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17728             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17729             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17730             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17731             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17732              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17733                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17734             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17735             "</li>"];
17736
17737         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17738             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17739                                 n.nextSibling.ui.getEl(), buf.join(""));
17740         }else{
17741             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17742         }
17743
17744         this.elNode = this.wrap.childNodes[0];
17745         this.ctNode = this.wrap.childNodes[1];
17746         var cs = this.elNode.childNodes;
17747         this.indentNode = cs[0];
17748         this.ecNode = cs[1];
17749         this.iconNode = cs[2];
17750         var index = 3;
17751         if(cb){
17752             this.checkbox = cs[3];
17753             index++;
17754         }
17755         this.anchor = cs[index];
17756         this.textNode = cs[index].firstChild;
17757     },
17758
17759     getAnchor : function(){
17760         return this.anchor;
17761     },
17762
17763     getTextEl : function(){
17764         return this.textNode;
17765     },
17766
17767     getIconEl : function(){
17768         return this.iconNode;
17769     },
17770
17771     isChecked : function(){
17772         return this.checkbox ? this.checkbox.checked : false;
17773     },
17774
17775     updateExpandIcon : function(){
17776         if(this.rendered){
17777             var n = this.node, c1, c2;
17778             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17779             var hasChild = n.hasChildNodes();
17780             if(hasChild){
17781                 if(n.expanded){
17782                     cls += "-minus";
17783                     c1 = "x-tree-node-collapsed";
17784                     c2 = "x-tree-node-expanded";
17785                 }else{
17786                     cls += "-plus";
17787                     c1 = "x-tree-node-expanded";
17788                     c2 = "x-tree-node-collapsed";
17789                 }
17790                 if(this.wasLeaf){
17791                     this.removeClass("x-tree-node-leaf");
17792                     this.wasLeaf = false;
17793                 }
17794                 if(this.c1 != c1 || this.c2 != c2){
17795                     Roo.fly(this.elNode).replaceClass(c1, c2);
17796                     this.c1 = c1; this.c2 = c2;
17797                 }
17798             }else{
17799                 // this changes non-leafs into leafs if they have no children.
17800                 // it's not very rational behaviour..
17801                 
17802                 if(!this.wasLeaf && this.node.leaf){
17803                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17804                     delete this.c1;
17805                     delete this.c2;
17806                     this.wasLeaf = true;
17807                 }
17808             }
17809             var ecc = "x-tree-ec-icon "+cls;
17810             if(this.ecc != ecc){
17811                 this.ecNode.className = ecc;
17812                 this.ecc = ecc;
17813             }
17814         }
17815     },
17816
17817     getChildIndent : function(){
17818         if(!this.childIndent){
17819             var buf = [];
17820             var p = this.node;
17821             while(p){
17822                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17823                     if(!p.isLast()) {
17824                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17825                     } else {
17826                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17827                     }
17828                 }
17829                 p = p.parentNode;
17830             }
17831             this.childIndent = buf.join("");
17832         }
17833         return this.childIndent;
17834     },
17835
17836     renderIndent : function(){
17837         if(this.rendered){
17838             var indent = "";
17839             var p = this.node.parentNode;
17840             if(p){
17841                 indent = p.ui.getChildIndent();
17842             }
17843             if(this.indentMarkup != indent){ // don't rerender if not required
17844                 this.indentNode.innerHTML = indent;
17845                 this.indentMarkup = indent;
17846             }
17847             this.updateExpandIcon();
17848         }
17849     }
17850 };
17851
17852 Roo.tree.RootTreeNodeUI = function(){
17853     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17854 };
17855 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17856     render : function(){
17857         if(!this.rendered){
17858             var targetNode = this.node.ownerTree.innerCt.dom;
17859             this.node.expanded = true;
17860             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17861             this.wrap = this.ctNode = targetNode.firstChild;
17862         }
17863     },
17864     collapse : function(){
17865     },
17866     expand : function(){
17867     }
17868 });/*
17869  * Based on:
17870  * Ext JS Library 1.1.1
17871  * Copyright(c) 2006-2007, Ext JS, LLC.
17872  *
17873  * Originally Released Under LGPL - original licence link has changed is not relivant.
17874  *
17875  * Fork - LGPL
17876  * <script type="text/javascript">
17877  */
17878 /**
17879  * @class Roo.tree.TreeLoader
17880  * @extends Roo.util.Observable
17881  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17882  * nodes from a specified URL. The response must be a javascript Array definition
17883  * who's elements are node definition objects. eg:
17884  * <pre><code>
17885 {  success : true,
17886    data :      [
17887    
17888     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17889     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17890     ]
17891 }
17892
17893
17894 </code></pre>
17895  * <br><br>
17896  * The old style respose with just an array is still supported, but not recommended.
17897  * <br><br>
17898  *
17899  * A server request is sent, and child nodes are loaded only when a node is expanded.
17900  * The loading node's id is passed to the server under the parameter name "node" to
17901  * enable the server to produce the correct child nodes.
17902  * <br><br>
17903  * To pass extra parameters, an event handler may be attached to the "beforeload"
17904  * event, and the parameters specified in the TreeLoader's baseParams property:
17905  * <pre><code>
17906     myTreeLoader.on("beforeload", function(treeLoader, node) {
17907         this.baseParams.category = node.attributes.category;
17908     }, this);
17909 </code></pre><
17910  * This would pass an HTTP parameter called "category" to the server containing
17911  * the value of the Node's "category" attribute.
17912  * @constructor
17913  * Creates a new Treeloader.
17914  * @param {Object} config A config object containing config properties.
17915  */
17916 Roo.tree.TreeLoader = function(config){
17917     this.baseParams = {};
17918     this.requestMethod = "POST";
17919     Roo.apply(this, config);
17920
17921     this.addEvents({
17922     
17923         /**
17924          * @event beforeload
17925          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17926          * @param {Object} This TreeLoader object.
17927          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17928          * @param {Object} callback The callback function specified in the {@link #load} call.
17929          */
17930         beforeload : true,
17931         /**
17932          * @event load
17933          * Fires when the node has been successfuly loaded.
17934          * @param {Object} This TreeLoader object.
17935          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17936          * @param {Object} response The response object containing the data from the server.
17937          */
17938         load : true,
17939         /**
17940          * @event loadexception
17941          * Fires if the network request failed.
17942          * @param {Object} This TreeLoader object.
17943          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17944          * @param {Object} response The response object containing the data from the server.
17945          */
17946         loadexception : true,
17947         /**
17948          * @event create
17949          * Fires before a node is created, enabling you to return custom Node types 
17950          * @param {Object} This TreeLoader object.
17951          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17952          */
17953         create : true
17954     });
17955
17956     Roo.tree.TreeLoader.superclass.constructor.call(this);
17957 };
17958
17959 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17960     /**
17961     * @cfg {String} dataUrl The URL from which to request a Json string which
17962     * specifies an array of node definition object representing the child nodes
17963     * to be loaded.
17964     */
17965     /**
17966     * @cfg {String} requestMethod either GET or POST
17967     * defaults to POST (due to BC)
17968     * to be loaded.
17969     */
17970     /**
17971     * @cfg {Object} baseParams (optional) An object containing properties which
17972     * specify HTTP parameters to be passed to each request for child nodes.
17973     */
17974     /**
17975     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17976     * created by this loader. If the attributes sent by the server have an attribute in this object,
17977     * they take priority.
17978     */
17979     /**
17980     * @cfg {Object} uiProviders (optional) An object containing properties which
17981     * 
17982     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17983     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17984     * <i>uiProvider</i> attribute of a returned child node is a string rather
17985     * than a reference to a TreeNodeUI implementation, this that string value
17986     * is used as a property name in the uiProviders object. You can define the provider named
17987     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17988     */
17989     uiProviders : {},
17990
17991     /**
17992     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17993     * child nodes before loading.
17994     */
17995     clearOnLoad : true,
17996
17997     /**
17998     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
17999     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18000     * Grid query { data : [ .....] }
18001     */
18002     
18003     root : false,
18004      /**
18005     * @cfg {String} queryParam (optional) 
18006     * Name of the query as it will be passed on the querystring (defaults to 'node')
18007     * eg. the request will be ?node=[id]
18008     */
18009     
18010     
18011     queryParam: false,
18012     
18013     /**
18014      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18015      * This is called automatically when a node is expanded, but may be used to reload
18016      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18017      * @param {Roo.tree.TreeNode} node
18018      * @param {Function} callback
18019      */
18020     load : function(node, callback){
18021         if(this.clearOnLoad){
18022             while(node.firstChild){
18023                 node.removeChild(node.firstChild);
18024             }
18025         }
18026         if(node.attributes.children){ // preloaded json children
18027             var cs = node.attributes.children;
18028             for(var i = 0, len = cs.length; i < len; i++){
18029                 node.appendChild(this.createNode(cs[i]));
18030             }
18031             if(typeof callback == "function"){
18032                 callback();
18033             }
18034         }else if(this.dataUrl){
18035             this.requestData(node, callback);
18036         }
18037     },
18038
18039     getParams: function(node){
18040         var buf = [], bp = this.baseParams;
18041         for(var key in bp){
18042             if(typeof bp[key] != "function"){
18043                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18044             }
18045         }
18046         var n = this.queryParam === false ? 'node' : this.queryParam;
18047         buf.push(n + "=", encodeURIComponent(node.id));
18048         return buf.join("");
18049     },
18050
18051     requestData : function(node, callback){
18052         if(this.fireEvent("beforeload", this, node, callback) !== false){
18053             this.transId = Roo.Ajax.request({
18054                 method:this.requestMethod,
18055                 url: this.dataUrl||this.url,
18056                 success: this.handleResponse,
18057                 failure: this.handleFailure,
18058                 scope: this,
18059                 argument: {callback: callback, node: node},
18060                 params: this.getParams(node)
18061             });
18062         }else{
18063             // if the load is cancelled, make sure we notify
18064             // the node that we are done
18065             if(typeof callback == "function"){
18066                 callback();
18067             }
18068         }
18069     },
18070
18071     isLoading : function(){
18072         return this.transId ? true : false;
18073     },
18074
18075     abort : function(){
18076         if(this.isLoading()){
18077             Roo.Ajax.abort(this.transId);
18078         }
18079     },
18080
18081     // private
18082     createNode : function(attr)
18083     {
18084         // apply baseAttrs, nice idea Corey!
18085         if(this.baseAttrs){
18086             Roo.applyIf(attr, this.baseAttrs);
18087         }
18088         if(this.applyLoader !== false){
18089             attr.loader = this;
18090         }
18091         // uiProvider = depreciated..
18092         
18093         if(typeof(attr.uiProvider) == 'string'){
18094            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18095                 /**  eval:var:attr */ eval(attr.uiProvider);
18096         }
18097         if(typeof(this.uiProviders['default']) != 'undefined') {
18098             attr.uiProvider = this.uiProviders['default'];
18099         }
18100         
18101         this.fireEvent('create', this, attr);
18102         
18103         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18104         return(attr.leaf ?
18105                         new Roo.tree.TreeNode(attr) :
18106                         new Roo.tree.AsyncTreeNode(attr));
18107     },
18108
18109     processResponse : function(response, node, callback)
18110     {
18111         var json = response.responseText;
18112         try {
18113             
18114             var o = Roo.decode(json);
18115             
18116             if (this.root === false && typeof(o.success) != undefined) {
18117                 this.root = 'data'; // the default behaviour for list like data..
18118                 }
18119                 
18120             if (this.root !== false &&  !o.success) {
18121                 // it's a failure condition.
18122                 var a = response.argument;
18123                 this.fireEvent("loadexception", this, a.node, response);
18124                 Roo.log("Load failed - should have a handler really");
18125                 return;
18126             }
18127             
18128             
18129             
18130             if (this.root !== false) {
18131                  o = o[this.root];
18132             }
18133             
18134             for(var i = 0, len = o.length; i < len; i++){
18135                 var n = this.createNode(o[i]);
18136                 if(n){
18137                     node.appendChild(n);
18138                 }
18139             }
18140             if(typeof callback == "function"){
18141                 callback(this, node);
18142             }
18143         }catch(e){
18144             this.handleFailure(response);
18145         }
18146     },
18147
18148     handleResponse : function(response){
18149         this.transId = false;
18150         var a = response.argument;
18151         this.processResponse(response, a.node, a.callback);
18152         this.fireEvent("load", this, a.node, response);
18153     },
18154
18155     handleFailure : function(response)
18156     {
18157         // should handle failure better..
18158         this.transId = false;
18159         var a = response.argument;
18160         this.fireEvent("loadexception", this, a.node, response);
18161         if(typeof a.callback == "function"){
18162             a.callback(this, a.node);
18163         }
18164     }
18165 });/*
18166  * Based on:
18167  * Ext JS Library 1.1.1
18168  * Copyright(c) 2006-2007, Ext JS, LLC.
18169  *
18170  * Originally Released Under LGPL - original licence link has changed is not relivant.
18171  *
18172  * Fork - LGPL
18173  * <script type="text/javascript">
18174  */
18175
18176 /**
18177 * @class Roo.tree.TreeFilter
18178 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18179 * @param {TreePanel} tree
18180 * @param {Object} config (optional)
18181  */
18182 Roo.tree.TreeFilter = function(tree, config){
18183     this.tree = tree;
18184     this.filtered = {};
18185     Roo.apply(this, config);
18186 };
18187
18188 Roo.tree.TreeFilter.prototype = {
18189     clearBlank:false,
18190     reverse:false,
18191     autoClear:false,
18192     remove:false,
18193
18194      /**
18195      * Filter the data by a specific attribute.
18196      * @param {String/RegExp} value Either string that the attribute value
18197      * should start with or a RegExp to test against the attribute
18198      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18199      * @param {TreeNode} startNode (optional) The node to start the filter at.
18200      */
18201     filter : function(value, attr, startNode){
18202         attr = attr || "text";
18203         var f;
18204         if(typeof value == "string"){
18205             var vlen = value.length;
18206             // auto clear empty filter
18207             if(vlen == 0 && this.clearBlank){
18208                 this.clear();
18209                 return;
18210             }
18211             value = value.toLowerCase();
18212             f = function(n){
18213                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18214             };
18215         }else if(value.exec){ // regex?
18216             f = function(n){
18217                 return value.test(n.attributes[attr]);
18218             };
18219         }else{
18220             throw 'Illegal filter type, must be string or regex';
18221         }
18222         this.filterBy(f, null, startNode);
18223         },
18224
18225     /**
18226      * Filter by a function. The passed function will be called with each
18227      * node in the tree (or from the startNode). If the function returns true, the node is kept
18228      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18229      * @param {Function} fn The filter function
18230      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18231      */
18232     filterBy : function(fn, scope, startNode){
18233         startNode = startNode || this.tree.root;
18234         if(this.autoClear){
18235             this.clear();
18236         }
18237         var af = this.filtered, rv = this.reverse;
18238         var f = function(n){
18239             if(n == startNode){
18240                 return true;
18241             }
18242             if(af[n.id]){
18243                 return false;
18244             }
18245             var m = fn.call(scope || n, n);
18246             if(!m || rv){
18247                 af[n.id] = n;
18248                 n.ui.hide();
18249                 return false;
18250             }
18251             return true;
18252         };
18253         startNode.cascade(f);
18254         if(this.remove){
18255            for(var id in af){
18256                if(typeof id != "function"){
18257                    var n = af[id];
18258                    if(n && n.parentNode){
18259                        n.parentNode.removeChild(n);
18260                    }
18261                }
18262            }
18263         }
18264     },
18265
18266     /**
18267      * Clears the current filter. Note: with the "remove" option
18268      * set a filter cannot be cleared.
18269      */
18270     clear : function(){
18271         var t = this.tree;
18272         var af = this.filtered;
18273         for(var id in af){
18274             if(typeof id != "function"){
18275                 var n = af[id];
18276                 if(n){
18277                     n.ui.show();
18278                 }
18279             }
18280         }
18281         this.filtered = {};
18282     }
18283 };
18284 /*
18285  * Based on:
18286  * Ext JS Library 1.1.1
18287  * Copyright(c) 2006-2007, Ext JS, LLC.
18288  *
18289  * Originally Released Under LGPL - original licence link has changed is not relivant.
18290  *
18291  * Fork - LGPL
18292  * <script type="text/javascript">
18293  */
18294  
18295
18296 /**
18297  * @class Roo.tree.TreeSorter
18298  * Provides sorting of nodes in a TreePanel
18299  * 
18300  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18301  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18302  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18303  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18304  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18305  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18306  * @constructor
18307  * @param {TreePanel} tree
18308  * @param {Object} config
18309  */
18310 Roo.tree.TreeSorter = function(tree, config){
18311     Roo.apply(this, config);
18312     tree.on("beforechildrenrendered", this.doSort, this);
18313     tree.on("append", this.updateSort, this);
18314     tree.on("insert", this.updateSort, this);
18315     
18316     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18317     var p = this.property || "text";
18318     var sortType = this.sortType;
18319     var fs = this.folderSort;
18320     var cs = this.caseSensitive === true;
18321     var leafAttr = this.leafAttr || 'leaf';
18322
18323     this.sortFn = function(n1, n2){
18324         if(fs){
18325             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18326                 return 1;
18327             }
18328             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18329                 return -1;
18330             }
18331         }
18332         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18333         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18334         if(v1 < v2){
18335                         return dsc ? +1 : -1;
18336                 }else if(v1 > v2){
18337                         return dsc ? -1 : +1;
18338         }else{
18339                 return 0;
18340         }
18341     };
18342 };
18343
18344 Roo.tree.TreeSorter.prototype = {
18345     doSort : function(node){
18346         node.sort(this.sortFn);
18347     },
18348     
18349     compareNodes : function(n1, n2){
18350         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18351     },
18352     
18353     updateSort : function(tree, node){
18354         if(node.childrenRendered){
18355             this.doSort.defer(1, this, [node]);
18356         }
18357     }
18358 };/*
18359  * Based on:
18360  * Ext JS Library 1.1.1
18361  * Copyright(c) 2006-2007, Ext JS, LLC.
18362  *
18363  * Originally Released Under LGPL - original licence link has changed is not relivant.
18364  *
18365  * Fork - LGPL
18366  * <script type="text/javascript">
18367  */
18368
18369 if(Roo.dd.DropZone){
18370     
18371 Roo.tree.TreeDropZone = function(tree, config){
18372     this.allowParentInsert = false;
18373     this.allowContainerDrop = false;
18374     this.appendOnly = false;
18375     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18376     this.tree = tree;
18377     this.lastInsertClass = "x-tree-no-status";
18378     this.dragOverData = {};
18379 };
18380
18381 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18382     ddGroup : "TreeDD",
18383     scroll:  true,
18384     
18385     expandDelay : 1000,
18386     
18387     expandNode : function(node){
18388         if(node.hasChildNodes() && !node.isExpanded()){
18389             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18390         }
18391     },
18392     
18393     queueExpand : function(node){
18394         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18395     },
18396     
18397     cancelExpand : function(){
18398         if(this.expandProcId){
18399             clearTimeout(this.expandProcId);
18400             this.expandProcId = false;
18401         }
18402     },
18403     
18404     isValidDropPoint : function(n, pt, dd, e, data){
18405         if(!n || !data){ return false; }
18406         var targetNode = n.node;
18407         var dropNode = data.node;
18408         // default drop rules
18409         if(!(targetNode && targetNode.isTarget && pt)){
18410             return false;
18411         }
18412         if(pt == "append" && targetNode.allowChildren === false){
18413             return false;
18414         }
18415         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18416             return false;
18417         }
18418         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18419             return false;
18420         }
18421         // reuse the object
18422         var overEvent = this.dragOverData;
18423         overEvent.tree = this.tree;
18424         overEvent.target = targetNode;
18425         overEvent.data = data;
18426         overEvent.point = pt;
18427         overEvent.source = dd;
18428         overEvent.rawEvent = e;
18429         overEvent.dropNode = dropNode;
18430         overEvent.cancel = false;  
18431         var result = this.tree.fireEvent("nodedragover", overEvent);
18432         return overEvent.cancel === false && result !== false;
18433     },
18434     
18435     getDropPoint : function(e, n, dd)
18436     {
18437         var tn = n.node;
18438         if(tn.isRoot){
18439             return tn.allowChildren !== false ? "append" : false; // always append for root
18440         }
18441         var dragEl = n.ddel;
18442         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18443         var y = Roo.lib.Event.getPageY(e);
18444         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18445         
18446         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18447         var noAppend = tn.allowChildren === false;
18448         if(this.appendOnly || tn.parentNode.allowChildren === false){
18449             return noAppend ? false : "append";
18450         }
18451         var noBelow = false;
18452         if(!this.allowParentInsert){
18453             noBelow = tn.hasChildNodes() && tn.isExpanded();
18454         }
18455         var q = (b - t) / (noAppend ? 2 : 3);
18456         if(y >= t && y < (t + q)){
18457             return "above";
18458         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18459             return "below";
18460         }else{
18461             return "append";
18462         }
18463     },
18464     
18465     onNodeEnter : function(n, dd, e, data)
18466     {
18467         this.cancelExpand();
18468     },
18469     
18470     onNodeOver : function(n, dd, e, data)
18471     {
18472        
18473         var pt = this.getDropPoint(e, n, dd);
18474         var node = n.node;
18475         
18476         // auto node expand check
18477         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18478             this.queueExpand(node);
18479         }else if(pt != "append"){
18480             this.cancelExpand();
18481         }
18482         
18483         // set the insert point style on the target node
18484         var returnCls = this.dropNotAllowed;
18485         if(this.isValidDropPoint(n, pt, dd, e, data)){
18486            if(pt){
18487                var el = n.ddel;
18488                var cls;
18489                if(pt == "above"){
18490                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18491                    cls = "x-tree-drag-insert-above";
18492                }else if(pt == "below"){
18493                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18494                    cls = "x-tree-drag-insert-below";
18495                }else{
18496                    returnCls = "x-tree-drop-ok-append";
18497                    cls = "x-tree-drag-append";
18498                }
18499                if(this.lastInsertClass != cls){
18500                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18501                    this.lastInsertClass = cls;
18502                }
18503            }
18504        }
18505        return returnCls;
18506     },
18507     
18508     onNodeOut : function(n, dd, e, data){
18509         
18510         this.cancelExpand();
18511         this.removeDropIndicators(n);
18512     },
18513     
18514     onNodeDrop : function(n, dd, e, data){
18515         var point = this.getDropPoint(e, n, dd);
18516         var targetNode = n.node;
18517         targetNode.ui.startDrop();
18518         if(!this.isValidDropPoint(n, point, dd, e, data)){
18519             targetNode.ui.endDrop();
18520             return false;
18521         }
18522         // first try to find the drop node
18523         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18524         var dropEvent = {
18525             tree : this.tree,
18526             target: targetNode,
18527             data: data,
18528             point: point,
18529             source: dd,
18530             rawEvent: e,
18531             dropNode: dropNode,
18532             cancel: !dropNode   
18533         };
18534         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18535         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18536             targetNode.ui.endDrop();
18537             return false;
18538         }
18539         // allow target changing
18540         targetNode = dropEvent.target;
18541         if(point == "append" && !targetNode.isExpanded()){
18542             targetNode.expand(false, null, function(){
18543                 this.completeDrop(dropEvent);
18544             }.createDelegate(this));
18545         }else{
18546             this.completeDrop(dropEvent);
18547         }
18548         return true;
18549     },
18550     
18551     completeDrop : function(de){
18552         var ns = de.dropNode, p = de.point, t = de.target;
18553         if(!(ns instanceof Array)){
18554             ns = [ns];
18555         }
18556         var n;
18557         for(var i = 0, len = ns.length; i < len; i++){
18558             n = ns[i];
18559             if(p == "above"){
18560                 t.parentNode.insertBefore(n, t);
18561             }else if(p == "below"){
18562                 t.parentNode.insertBefore(n, t.nextSibling);
18563             }else{
18564                 t.appendChild(n);
18565             }
18566         }
18567         n.ui.focus();
18568         if(this.tree.hlDrop){
18569             n.ui.highlight();
18570         }
18571         t.ui.endDrop();
18572         this.tree.fireEvent("nodedrop", de);
18573     },
18574     
18575     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18576         if(this.tree.hlDrop){
18577             dropNode.ui.focus();
18578             dropNode.ui.highlight();
18579         }
18580         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18581     },
18582     
18583     getTree : function(){
18584         return this.tree;
18585     },
18586     
18587     removeDropIndicators : function(n){
18588         if(n && n.ddel){
18589             var el = n.ddel;
18590             Roo.fly(el).removeClass([
18591                     "x-tree-drag-insert-above",
18592                     "x-tree-drag-insert-below",
18593                     "x-tree-drag-append"]);
18594             this.lastInsertClass = "_noclass";
18595         }
18596     },
18597     
18598     beforeDragDrop : function(target, e, id){
18599         this.cancelExpand();
18600         return true;
18601     },
18602     
18603     afterRepair : function(data){
18604         if(data && Roo.enableFx){
18605             data.node.ui.highlight();
18606         }
18607         this.hideProxy();
18608     } 
18609     
18610 });
18611
18612 }
18613 /*
18614  * Based on:
18615  * Ext JS Library 1.1.1
18616  * Copyright(c) 2006-2007, Ext JS, LLC.
18617  *
18618  * Originally Released Under LGPL - original licence link has changed is not relivant.
18619  *
18620  * Fork - LGPL
18621  * <script type="text/javascript">
18622  */
18623  
18624
18625 if(Roo.dd.DragZone){
18626 Roo.tree.TreeDragZone = function(tree, config){
18627     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18628     this.tree = tree;
18629 };
18630
18631 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18632     ddGroup : "TreeDD",
18633    
18634     onBeforeDrag : function(data, e){
18635         var n = data.node;
18636         return n && n.draggable && !n.disabled;
18637     },
18638      
18639     
18640     onInitDrag : function(e){
18641         var data = this.dragData;
18642         this.tree.getSelectionModel().select(data.node);
18643         this.proxy.update("");
18644         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18645         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18646     },
18647     
18648     getRepairXY : function(e, data){
18649         return data.node.ui.getDDRepairXY();
18650     },
18651     
18652     onEndDrag : function(data, e){
18653         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18654         
18655         
18656     },
18657     
18658     onValidDrop : function(dd, e, id){
18659         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18660         this.hideProxy();
18661     },
18662     
18663     beforeInvalidDrop : function(e, id){
18664         // this scrolls the original position back into view
18665         var sm = this.tree.getSelectionModel();
18666         sm.clearSelections();
18667         sm.select(this.dragData.node);
18668     }
18669 });
18670 }/*
18671  * Based on:
18672  * Ext JS Library 1.1.1
18673  * Copyright(c) 2006-2007, Ext JS, LLC.
18674  *
18675  * Originally Released Under LGPL - original licence link has changed is not relivant.
18676  *
18677  * Fork - LGPL
18678  * <script type="text/javascript">
18679  */
18680 /**
18681  * @class Roo.tree.TreeEditor
18682  * @extends Roo.Editor
18683  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18684  * as the editor field.
18685  * @constructor
18686  * @param {Object} config (used to be the tree panel.)
18687  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18688  * 
18689  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18690  * @cfg {Roo.form.TextField|Object} field The field configuration
18691  *
18692  * 
18693  */
18694 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18695     var tree = config;
18696     var field;
18697     if (oldconfig) { // old style..
18698         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18699     } else {
18700         // new style..
18701         tree = config.tree;
18702         config.field = config.field  || {};
18703         config.field.xtype = 'TextField';
18704         field = Roo.factory(config.field, Roo.form);
18705     }
18706     config = config || {};
18707     
18708     
18709     this.addEvents({
18710         /**
18711          * @event beforenodeedit
18712          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18713          * false from the handler of this event.
18714          * @param {Editor} this
18715          * @param {Roo.tree.Node} node 
18716          */
18717         "beforenodeedit" : true
18718     });
18719     
18720     //Roo.log(config);
18721     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18722
18723     this.tree = tree;
18724
18725     tree.on('beforeclick', this.beforeNodeClick, this);
18726     tree.getTreeEl().on('mousedown', this.hide, this);
18727     this.on('complete', this.updateNode, this);
18728     this.on('beforestartedit', this.fitToTree, this);
18729     this.on('startedit', this.bindScroll, this, {delay:10});
18730     this.on('specialkey', this.onSpecialKey, this);
18731 };
18732
18733 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18734     /**
18735      * @cfg {String} alignment
18736      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18737      */
18738     alignment: "l-l",
18739     // inherit
18740     autoSize: false,
18741     /**
18742      * @cfg {Boolean} hideEl
18743      * True to hide the bound element while the editor is displayed (defaults to false)
18744      */
18745     hideEl : false,
18746     /**
18747      * @cfg {String} cls
18748      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18749      */
18750     cls: "x-small-editor x-tree-editor",
18751     /**
18752      * @cfg {Boolean} shim
18753      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18754      */
18755     shim:false,
18756     // inherit
18757     shadow:"frame",
18758     /**
18759      * @cfg {Number} maxWidth
18760      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18761      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18762      * scroll and client offsets into account prior to each edit.
18763      */
18764     maxWidth: 250,
18765
18766     editDelay : 350,
18767
18768     // private
18769     fitToTree : function(ed, el){
18770         var td = this.tree.getTreeEl().dom, nd = el.dom;
18771         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18772             td.scrollLeft = nd.offsetLeft;
18773         }
18774         var w = Math.min(
18775                 this.maxWidth,
18776                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18777         this.setSize(w, '');
18778         
18779         return this.fireEvent('beforenodeedit', this, this.editNode);
18780         
18781     },
18782
18783     // private
18784     triggerEdit : function(node){
18785         this.completeEdit();
18786         this.editNode = node;
18787         this.startEdit(node.ui.textNode, node.text);
18788     },
18789
18790     // private
18791     bindScroll : function(){
18792         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18793     },
18794
18795     // private
18796     beforeNodeClick : function(node, e){
18797         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18798         this.lastClick = new Date();
18799         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18800             e.stopEvent();
18801             this.triggerEdit(node);
18802             return false;
18803         }
18804         return true;
18805     },
18806
18807     // private
18808     updateNode : function(ed, value){
18809         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18810         this.editNode.setText(value);
18811     },
18812
18813     // private
18814     onHide : function(){
18815         Roo.tree.TreeEditor.superclass.onHide.call(this);
18816         if(this.editNode){
18817             this.editNode.ui.focus();
18818         }
18819     },
18820
18821     // private
18822     onSpecialKey : function(field, e){
18823         var k = e.getKey();
18824         if(k == e.ESC){
18825             e.stopEvent();
18826             this.cancelEdit();
18827         }else if(k == e.ENTER && !e.hasModifier()){
18828             e.stopEvent();
18829             this.completeEdit();
18830         }
18831     }
18832 });//<Script type="text/javascript">
18833 /*
18834  * Based on:
18835  * Ext JS Library 1.1.1
18836  * Copyright(c) 2006-2007, Ext JS, LLC.
18837  *
18838  * Originally Released Under LGPL - original licence link has changed is not relivant.
18839  *
18840  * Fork - LGPL
18841  * <script type="text/javascript">
18842  */
18843  
18844 /**
18845  * Not documented??? - probably should be...
18846  */
18847
18848 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18849     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18850     
18851     renderElements : function(n, a, targetNode, bulkRender){
18852         //consel.log("renderElements?");
18853         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18854
18855         var t = n.getOwnerTree();
18856         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18857         
18858         var cols = t.columns;
18859         var bw = t.borderWidth;
18860         var c = cols[0];
18861         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18862          var cb = typeof a.checked == "boolean";
18863         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18864         var colcls = 'x-t-' + tid + '-c0';
18865         var buf = [
18866             '<li class="x-tree-node">',
18867             
18868                 
18869                 '<div class="x-tree-node-el ', a.cls,'">',
18870                     // extran...
18871                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18872                 
18873                 
18874                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18875                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18876                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18877                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18878                            (a.iconCls ? ' '+a.iconCls : ''),
18879                            '" unselectable="on" />',
18880                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18881                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18882                              
18883                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18884                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18885                             '<span unselectable="on" qtip="' + tx + '">',
18886                              tx,
18887                              '</span></a>' ,
18888                     '</div>',
18889                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18890                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18891                  ];
18892         for(var i = 1, len = cols.length; i < len; i++){
18893             c = cols[i];
18894             colcls = 'x-t-' + tid + '-c' +i;
18895             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18896             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18897                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18898                       "</div>");
18899          }
18900          
18901          buf.push(
18902             '</a>',
18903             '<div class="x-clear"></div></div>',
18904             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18905             "</li>");
18906         
18907         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18908             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18909                                 n.nextSibling.ui.getEl(), buf.join(""));
18910         }else{
18911             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18912         }
18913         var el = this.wrap.firstChild;
18914         this.elRow = el;
18915         this.elNode = el.firstChild;
18916         this.ranchor = el.childNodes[1];
18917         this.ctNode = this.wrap.childNodes[1];
18918         var cs = el.firstChild.childNodes;
18919         this.indentNode = cs[0];
18920         this.ecNode = cs[1];
18921         this.iconNode = cs[2];
18922         var index = 3;
18923         if(cb){
18924             this.checkbox = cs[3];
18925             index++;
18926         }
18927         this.anchor = cs[index];
18928         
18929         this.textNode = cs[index].firstChild;
18930         
18931         //el.on("click", this.onClick, this);
18932         //el.on("dblclick", this.onDblClick, this);
18933         
18934         
18935        // console.log(this);
18936     },
18937     initEvents : function(){
18938         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18939         
18940             
18941         var a = this.ranchor;
18942
18943         var el = Roo.get(a);
18944
18945         if(Roo.isOpera){ // opera render bug ignores the CSS
18946             el.setStyle("text-decoration", "none");
18947         }
18948
18949         el.on("click", this.onClick, this);
18950         el.on("dblclick", this.onDblClick, this);
18951         el.on("contextmenu", this.onContextMenu, this);
18952         
18953     },
18954     
18955     /*onSelectedChange : function(state){
18956         if(state){
18957             this.focus();
18958             this.addClass("x-tree-selected");
18959         }else{
18960             //this.blur();
18961             this.removeClass("x-tree-selected");
18962         }
18963     },*/
18964     addClass : function(cls){
18965         if(this.elRow){
18966             Roo.fly(this.elRow).addClass(cls);
18967         }
18968         
18969     },
18970     
18971     
18972     removeClass : function(cls){
18973         if(this.elRow){
18974             Roo.fly(this.elRow).removeClass(cls);
18975         }
18976     }
18977
18978     
18979     
18980 });//<Script type="text/javascript">
18981
18982 /*
18983  * Based on:
18984  * Ext JS Library 1.1.1
18985  * Copyright(c) 2006-2007, Ext JS, LLC.
18986  *
18987  * Originally Released Under LGPL - original licence link has changed is not relivant.
18988  *
18989  * Fork - LGPL
18990  * <script type="text/javascript">
18991  */
18992  
18993
18994 /**
18995  * @class Roo.tree.ColumnTree
18996  * @extends Roo.data.TreePanel
18997  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
18998  * @cfg {int} borderWidth  compined right/left border allowance
18999  * @constructor
19000  * @param {String/HTMLElement/Element} el The container element
19001  * @param {Object} config
19002  */
19003 Roo.tree.ColumnTree =  function(el, config)
19004 {
19005    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19006    this.addEvents({
19007         /**
19008         * @event resize
19009         * Fire this event on a container when it resizes
19010         * @param {int} w Width
19011         * @param {int} h Height
19012         */
19013        "resize" : true
19014     });
19015     this.on('resize', this.onResize, this);
19016 };
19017
19018 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19019     //lines:false,
19020     
19021     
19022     borderWidth: Roo.isBorderBox ? 0 : 2, 
19023     headEls : false,
19024     
19025     render : function(){
19026         // add the header.....
19027        
19028         Roo.tree.ColumnTree.superclass.render.apply(this);
19029         
19030         this.el.addClass('x-column-tree');
19031         
19032         this.headers = this.el.createChild(
19033             {cls:'x-tree-headers'},this.innerCt.dom);
19034    
19035         var cols = this.columns, c;
19036         var totalWidth = 0;
19037         this.headEls = [];
19038         var  len = cols.length;
19039         for(var i = 0; i < len; i++){
19040              c = cols[i];
19041              totalWidth += c.width;
19042             this.headEls.push(this.headers.createChild({
19043                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19044                  cn: {
19045                      cls:'x-tree-hd-text',
19046                      html: c.header
19047                  },
19048                  style:'width:'+(c.width-this.borderWidth)+'px;'
19049              }));
19050         }
19051         this.headers.createChild({cls:'x-clear'});
19052         // prevent floats from wrapping when clipped
19053         this.headers.setWidth(totalWidth);
19054         //this.innerCt.setWidth(totalWidth);
19055         this.innerCt.setStyle({ overflow: 'auto' });
19056         this.onResize(this.width, this.height);
19057              
19058         
19059     },
19060     onResize : function(w,h)
19061     {
19062         this.height = h;
19063         this.width = w;
19064         // resize cols..
19065         this.innerCt.setWidth(this.width);
19066         this.innerCt.setHeight(this.height-20);
19067         
19068         // headers...
19069         var cols = this.columns, c;
19070         var totalWidth = 0;
19071         var expEl = false;
19072         var len = cols.length;
19073         for(var i = 0; i < len; i++){
19074             c = cols[i];
19075             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19076                 // it's the expander..
19077                 expEl  = this.headEls[i];
19078                 continue;
19079             }
19080             totalWidth += c.width;
19081             
19082         }
19083         if (expEl) {
19084             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19085         }
19086         this.headers.setWidth(w-20);
19087
19088         
19089         
19090         
19091     }
19092 });
19093 /*
19094  * Based on:
19095  * Ext JS Library 1.1.1
19096  * Copyright(c) 2006-2007, Ext JS, LLC.
19097  *
19098  * Originally Released Under LGPL - original licence link has changed is not relivant.
19099  *
19100  * Fork - LGPL
19101  * <script type="text/javascript">
19102  */
19103  
19104 /**
19105  * @class Roo.menu.Menu
19106  * @extends Roo.util.Observable
19107  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19108  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19109  * @constructor
19110  * Creates a new Menu
19111  * @param {Object} config Configuration options
19112  */
19113 Roo.menu.Menu = function(config){
19114     Roo.apply(this, config);
19115     this.id = this.id || Roo.id();
19116     this.addEvents({
19117         /**
19118          * @event beforeshow
19119          * Fires before this menu is displayed
19120          * @param {Roo.menu.Menu} this
19121          */
19122         beforeshow : true,
19123         /**
19124          * @event beforehide
19125          * Fires before this menu is hidden
19126          * @param {Roo.menu.Menu} this
19127          */
19128         beforehide : true,
19129         /**
19130          * @event show
19131          * Fires after this menu is displayed
19132          * @param {Roo.menu.Menu} this
19133          */
19134         show : true,
19135         /**
19136          * @event hide
19137          * Fires after this menu is hidden
19138          * @param {Roo.menu.Menu} this
19139          */
19140         hide : true,
19141         /**
19142          * @event click
19143          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19144          * @param {Roo.menu.Menu} this
19145          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19146          * @param {Roo.EventObject} e
19147          */
19148         click : true,
19149         /**
19150          * @event mouseover
19151          * Fires when the mouse is hovering over this menu
19152          * @param {Roo.menu.Menu} this
19153          * @param {Roo.EventObject} e
19154          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19155          */
19156         mouseover : true,
19157         /**
19158          * @event mouseout
19159          * Fires when the mouse exits this menu
19160          * @param {Roo.menu.Menu} this
19161          * @param {Roo.EventObject} e
19162          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19163          */
19164         mouseout : true,
19165         /**
19166          * @event itemclick
19167          * Fires when a menu item contained in this menu is clicked
19168          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19169          * @param {Roo.EventObject} e
19170          */
19171         itemclick: true
19172     });
19173     if (this.registerMenu) {
19174         Roo.menu.MenuMgr.register(this);
19175     }
19176     
19177     var mis = this.items;
19178     this.items = new Roo.util.MixedCollection();
19179     if(mis){
19180         this.add.apply(this, mis);
19181     }
19182 };
19183
19184 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19185     /**
19186      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19187      */
19188     minWidth : 120,
19189     /**
19190      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19191      * for bottom-right shadow (defaults to "sides")
19192      */
19193     shadow : "sides",
19194     /**
19195      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19196      * this menu (defaults to "tl-tr?")
19197      */
19198     subMenuAlign : "tl-tr?",
19199     /**
19200      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19201      * relative to its element of origin (defaults to "tl-bl?")
19202      */
19203     defaultAlign : "tl-bl?",
19204     /**
19205      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19206      */
19207     allowOtherMenus : false,
19208     /**
19209      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19210      */
19211     registerMenu : true,
19212
19213     hidden:true,
19214
19215     // private
19216     render : function(){
19217         if(this.el){
19218             return;
19219         }
19220         var el = this.el = new Roo.Layer({
19221             cls: "x-menu",
19222             shadow:this.shadow,
19223             constrain: false,
19224             parentEl: this.parentEl || document.body,
19225             zindex:15000
19226         });
19227
19228         this.keyNav = new Roo.menu.MenuNav(this);
19229
19230         if(this.plain){
19231             el.addClass("x-menu-plain");
19232         }
19233         if(this.cls){
19234             el.addClass(this.cls);
19235         }
19236         // generic focus element
19237         this.focusEl = el.createChild({
19238             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19239         });
19240         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19241         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19242         
19243         ul.on("mouseover", this.onMouseOver, this);
19244         ul.on("mouseout", this.onMouseOut, this);
19245         this.items.each(function(item){
19246             if (item.hidden) {
19247                 return;
19248             }
19249             
19250             var li = document.createElement("li");
19251             li.className = "x-menu-list-item";
19252             ul.dom.appendChild(li);
19253             item.render(li, this);
19254         }, this);
19255         this.ul = ul;
19256         this.autoWidth();
19257     },
19258
19259     // private
19260     autoWidth : function(){
19261         var el = this.el, ul = this.ul;
19262         if(!el){
19263             return;
19264         }
19265         var w = this.width;
19266         if(w){
19267             el.setWidth(w);
19268         }else if(Roo.isIE){
19269             el.setWidth(this.minWidth);
19270             var t = el.dom.offsetWidth; // force recalc
19271             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19272         }
19273     },
19274
19275     // private
19276     delayAutoWidth : function(){
19277         if(this.rendered){
19278             if(!this.awTask){
19279                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19280             }
19281             this.awTask.delay(20);
19282         }
19283     },
19284
19285     // private
19286     findTargetItem : function(e){
19287         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19288         if(t && t.menuItemId){
19289             return this.items.get(t.menuItemId);
19290         }
19291     },
19292
19293     // private
19294     onClick : function(e){
19295         Roo.log("menu.onClick");
19296         var t = this.findTargetItem(e);
19297         if(!t){
19298             return;
19299         }
19300         Roo.log(e);
19301         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19302             if(t == this.activeItem && t.shouldDeactivate(e)){
19303                 this.activeItem.deactivate();
19304                 delete this.activeItem;
19305                 return;
19306             }
19307             if(t.canActivate){
19308                 this.setActiveItem(t, true);
19309             }
19310             return;
19311             
19312             
19313         }
19314         
19315         t.onClick(e);
19316         this.fireEvent("click", this, t, e);
19317     },
19318
19319     // private
19320     setActiveItem : function(item, autoExpand){
19321         if(item != this.activeItem){
19322             if(this.activeItem){
19323                 this.activeItem.deactivate();
19324             }
19325             this.activeItem = item;
19326             item.activate(autoExpand);
19327         }else if(autoExpand){
19328             item.expandMenu();
19329         }
19330     },
19331
19332     // private
19333     tryActivate : function(start, step){
19334         var items = this.items;
19335         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19336             var item = items.get(i);
19337             if(!item.disabled && item.canActivate){
19338                 this.setActiveItem(item, false);
19339                 return item;
19340             }
19341         }
19342         return false;
19343     },
19344
19345     // private
19346     onMouseOver : function(e){
19347         var t;
19348         if(t = this.findTargetItem(e)){
19349             if(t.canActivate && !t.disabled){
19350                 this.setActiveItem(t, true);
19351             }
19352         }
19353         this.fireEvent("mouseover", this, e, t);
19354     },
19355
19356     // private
19357     onMouseOut : function(e){
19358         var t;
19359         if(t = this.findTargetItem(e)){
19360             if(t == this.activeItem && t.shouldDeactivate(e)){
19361                 this.activeItem.deactivate();
19362                 delete this.activeItem;
19363             }
19364         }
19365         this.fireEvent("mouseout", this, e, t);
19366     },
19367
19368     /**
19369      * Read-only.  Returns true if the menu is currently displayed, else false.
19370      * @type Boolean
19371      */
19372     isVisible : function(){
19373         return this.el && !this.hidden;
19374     },
19375
19376     /**
19377      * Displays this menu relative to another element
19378      * @param {String/HTMLElement/Roo.Element} element The element to align to
19379      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19380      * the element (defaults to this.defaultAlign)
19381      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19382      */
19383     show : function(el, pos, parentMenu){
19384         this.parentMenu = parentMenu;
19385         if(!this.el){
19386             this.render();
19387         }
19388         this.fireEvent("beforeshow", this);
19389         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19390     },
19391
19392     /**
19393      * Displays this menu at a specific xy position
19394      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19395      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19396      */
19397     showAt : function(xy, parentMenu, /* private: */_e){
19398         this.parentMenu = parentMenu;
19399         if(!this.el){
19400             this.render();
19401         }
19402         if(_e !== false){
19403             this.fireEvent("beforeshow", this);
19404             xy = this.el.adjustForConstraints(xy);
19405         }
19406         this.el.setXY(xy);
19407         this.el.show();
19408         this.hidden = false;
19409         this.focus();
19410         this.fireEvent("show", this);
19411     },
19412
19413     focus : function(){
19414         if(!this.hidden){
19415             this.doFocus.defer(50, this);
19416         }
19417     },
19418
19419     doFocus : function(){
19420         if(!this.hidden){
19421             this.focusEl.focus();
19422         }
19423     },
19424
19425     /**
19426      * Hides this menu and optionally all parent menus
19427      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19428      */
19429     hide : function(deep){
19430         if(this.el && this.isVisible()){
19431             this.fireEvent("beforehide", this);
19432             if(this.activeItem){
19433                 this.activeItem.deactivate();
19434                 this.activeItem = null;
19435             }
19436             this.el.hide();
19437             this.hidden = true;
19438             this.fireEvent("hide", this);
19439         }
19440         if(deep === true && this.parentMenu){
19441             this.parentMenu.hide(true);
19442         }
19443     },
19444
19445     /**
19446      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19447      * Any of the following are valid:
19448      * <ul>
19449      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19450      * <li>An HTMLElement object which will be converted to a menu item</li>
19451      * <li>A menu item config object that will be created as a new menu item</li>
19452      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19453      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19454      * </ul>
19455      * Usage:
19456      * <pre><code>
19457 // Create the menu
19458 var menu = new Roo.menu.Menu();
19459
19460 // Create a menu item to add by reference
19461 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19462
19463 // Add a bunch of items at once using different methods.
19464 // Only the last item added will be returned.
19465 var item = menu.add(
19466     menuItem,                // add existing item by ref
19467     'Dynamic Item',          // new TextItem
19468     '-',                     // new separator
19469     { text: 'Config Item' }  // new item by config
19470 );
19471 </code></pre>
19472      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19473      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19474      */
19475     add : function(){
19476         var a = arguments, l = a.length, item;
19477         for(var i = 0; i < l; i++){
19478             var el = a[i];
19479             if ((typeof(el) == "object") && el.xtype && el.xns) {
19480                 el = Roo.factory(el, Roo.menu);
19481             }
19482             
19483             if(el.render){ // some kind of Item
19484                 item = this.addItem(el);
19485             }else if(typeof el == "string"){ // string
19486                 if(el == "separator" || el == "-"){
19487                     item = this.addSeparator();
19488                 }else{
19489                     item = this.addText(el);
19490                 }
19491             }else if(el.tagName || el.el){ // element
19492                 item = this.addElement(el);
19493             }else if(typeof el == "object"){ // must be menu item config?
19494                 item = this.addMenuItem(el);
19495             }
19496         }
19497         return item;
19498     },
19499
19500     /**
19501      * Returns this menu's underlying {@link Roo.Element} object
19502      * @return {Roo.Element} The element
19503      */
19504     getEl : function(){
19505         if(!this.el){
19506             this.render();
19507         }
19508         return this.el;
19509     },
19510
19511     /**
19512      * Adds a separator bar to the menu
19513      * @return {Roo.menu.Item} The menu item that was added
19514      */
19515     addSeparator : function(){
19516         return this.addItem(new Roo.menu.Separator());
19517     },
19518
19519     /**
19520      * Adds an {@link Roo.Element} object to the menu
19521      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19522      * @return {Roo.menu.Item} The menu item that was added
19523      */
19524     addElement : function(el){
19525         return this.addItem(new Roo.menu.BaseItem(el));
19526     },
19527
19528     /**
19529      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19530      * @param {Roo.menu.Item} item The menu item to add
19531      * @return {Roo.menu.Item} The menu item that was added
19532      */
19533     addItem : function(item){
19534         this.items.add(item);
19535         if(this.ul){
19536             var li = document.createElement("li");
19537             li.className = "x-menu-list-item";
19538             this.ul.dom.appendChild(li);
19539             item.render(li, this);
19540             this.delayAutoWidth();
19541         }
19542         return item;
19543     },
19544
19545     /**
19546      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19547      * @param {Object} config A MenuItem config object
19548      * @return {Roo.menu.Item} The menu item that was added
19549      */
19550     addMenuItem : function(config){
19551         if(!(config instanceof Roo.menu.Item)){
19552             if(typeof config.checked == "boolean"){ // must be check menu item config?
19553                 config = new Roo.menu.CheckItem(config);
19554             }else{
19555                 config = new Roo.menu.Item(config);
19556             }
19557         }
19558         return this.addItem(config);
19559     },
19560
19561     /**
19562      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19563      * @param {String} text The text to display in the menu item
19564      * @return {Roo.menu.Item} The menu item that was added
19565      */
19566     addText : function(text){
19567         return this.addItem(new Roo.menu.TextItem({ text : text }));
19568     },
19569
19570     /**
19571      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19572      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19573      * @param {Roo.menu.Item} item The menu item to add
19574      * @return {Roo.menu.Item} The menu item that was added
19575      */
19576     insert : function(index, item){
19577         this.items.insert(index, item);
19578         if(this.ul){
19579             var li = document.createElement("li");
19580             li.className = "x-menu-list-item";
19581             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19582             item.render(li, this);
19583             this.delayAutoWidth();
19584         }
19585         return item;
19586     },
19587
19588     /**
19589      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19590      * @param {Roo.menu.Item} item The menu item to remove
19591      */
19592     remove : function(item){
19593         this.items.removeKey(item.id);
19594         item.destroy();
19595     },
19596
19597     /**
19598      * Removes and destroys all items in the menu
19599      */
19600     removeAll : function(){
19601         var f;
19602         while(f = this.items.first()){
19603             this.remove(f);
19604         }
19605     }
19606 });
19607
19608 // MenuNav is a private utility class used internally by the Menu
19609 Roo.menu.MenuNav = function(menu){
19610     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19611     this.scope = this.menu = menu;
19612 };
19613
19614 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19615     doRelay : function(e, h){
19616         var k = e.getKey();
19617         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19618             this.menu.tryActivate(0, 1);
19619             return false;
19620         }
19621         return h.call(this.scope || this, e, this.menu);
19622     },
19623
19624     up : function(e, m){
19625         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19626             m.tryActivate(m.items.length-1, -1);
19627         }
19628     },
19629
19630     down : function(e, m){
19631         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19632             m.tryActivate(0, 1);
19633         }
19634     },
19635
19636     right : function(e, m){
19637         if(m.activeItem){
19638             m.activeItem.expandMenu(true);
19639         }
19640     },
19641
19642     left : function(e, m){
19643         m.hide();
19644         if(m.parentMenu && m.parentMenu.activeItem){
19645             m.parentMenu.activeItem.activate();
19646         }
19647     },
19648
19649     enter : function(e, m){
19650         if(m.activeItem){
19651             e.stopPropagation();
19652             m.activeItem.onClick(e);
19653             m.fireEvent("click", this, m.activeItem);
19654             return true;
19655         }
19656     }
19657 });/*
19658  * Based on:
19659  * Ext JS Library 1.1.1
19660  * Copyright(c) 2006-2007, Ext JS, LLC.
19661  *
19662  * Originally Released Under LGPL - original licence link has changed is not relivant.
19663  *
19664  * Fork - LGPL
19665  * <script type="text/javascript">
19666  */
19667  
19668 /**
19669  * @class Roo.menu.MenuMgr
19670  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19671  * @singleton
19672  */
19673 Roo.menu.MenuMgr = function(){
19674    var menus, active, groups = {}, attached = false, lastShow = new Date();
19675
19676    // private - called when first menu is created
19677    function init(){
19678        menus = {};
19679        active = new Roo.util.MixedCollection();
19680        Roo.get(document).addKeyListener(27, function(){
19681            if(active.length > 0){
19682                hideAll();
19683            }
19684        });
19685    }
19686
19687    // private
19688    function hideAll(){
19689        if(active && active.length > 0){
19690            var c = active.clone();
19691            c.each(function(m){
19692                m.hide();
19693            });
19694        }
19695    }
19696
19697    // private
19698    function onHide(m){
19699        active.remove(m);
19700        if(active.length < 1){
19701            Roo.get(document).un("mousedown", onMouseDown);
19702            attached = false;
19703        }
19704    }
19705
19706    // private
19707    function onShow(m){
19708        var last = active.last();
19709        lastShow = new Date();
19710        active.add(m);
19711        if(!attached){
19712            Roo.get(document).on("mousedown", onMouseDown);
19713            attached = true;
19714        }
19715        if(m.parentMenu){
19716           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19717           m.parentMenu.activeChild = m;
19718        }else if(last && last.isVisible()){
19719           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19720        }
19721    }
19722
19723    // private
19724    function onBeforeHide(m){
19725        if(m.activeChild){
19726            m.activeChild.hide();
19727        }
19728        if(m.autoHideTimer){
19729            clearTimeout(m.autoHideTimer);
19730            delete m.autoHideTimer;
19731        }
19732    }
19733
19734    // private
19735    function onBeforeShow(m){
19736        var pm = m.parentMenu;
19737        if(!pm && !m.allowOtherMenus){
19738            hideAll();
19739        }else if(pm && pm.activeChild && active != m){
19740            pm.activeChild.hide();
19741        }
19742    }
19743
19744    // private
19745    function onMouseDown(e){
19746        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19747            hideAll();
19748        }
19749    }
19750
19751    // private
19752    function onBeforeCheck(mi, state){
19753        if(state){
19754            var g = groups[mi.group];
19755            for(var i = 0, l = g.length; i < l; i++){
19756                if(g[i] != mi){
19757                    g[i].setChecked(false);
19758                }
19759            }
19760        }
19761    }
19762
19763    return {
19764
19765        /**
19766         * Hides all menus that are currently visible
19767         */
19768        hideAll : function(){
19769             hideAll();  
19770        },
19771
19772        // private
19773        register : function(menu){
19774            if(!menus){
19775                init();
19776            }
19777            menus[menu.id] = menu;
19778            menu.on("beforehide", onBeforeHide);
19779            menu.on("hide", onHide);
19780            menu.on("beforeshow", onBeforeShow);
19781            menu.on("show", onShow);
19782            var g = menu.group;
19783            if(g && menu.events["checkchange"]){
19784                if(!groups[g]){
19785                    groups[g] = [];
19786                }
19787                groups[g].push(menu);
19788                menu.on("checkchange", onCheck);
19789            }
19790        },
19791
19792         /**
19793          * Returns a {@link Roo.menu.Menu} object
19794          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19795          * be used to generate and return a new Menu instance.
19796          */
19797        get : function(menu){
19798            if(typeof menu == "string"){ // menu id
19799                return menus[menu];
19800            }else if(menu.events){  // menu instance
19801                return menu;
19802            }else if(typeof menu.length == 'number'){ // array of menu items?
19803                return new Roo.menu.Menu({items:menu});
19804            }else{ // otherwise, must be a config
19805                return new Roo.menu.Menu(menu);
19806            }
19807        },
19808
19809        // private
19810        unregister : function(menu){
19811            delete menus[menu.id];
19812            menu.un("beforehide", onBeforeHide);
19813            menu.un("hide", onHide);
19814            menu.un("beforeshow", onBeforeShow);
19815            menu.un("show", onShow);
19816            var g = menu.group;
19817            if(g && menu.events["checkchange"]){
19818                groups[g].remove(menu);
19819                menu.un("checkchange", onCheck);
19820            }
19821        },
19822
19823        // private
19824        registerCheckable : function(menuItem){
19825            var g = menuItem.group;
19826            if(g){
19827                if(!groups[g]){
19828                    groups[g] = [];
19829                }
19830                groups[g].push(menuItem);
19831                menuItem.on("beforecheckchange", onBeforeCheck);
19832            }
19833        },
19834
19835        // private
19836        unregisterCheckable : function(menuItem){
19837            var g = menuItem.group;
19838            if(g){
19839                groups[g].remove(menuItem);
19840                menuItem.un("beforecheckchange", onBeforeCheck);
19841            }
19842        }
19843    };
19844 }();/*
19845  * Based on:
19846  * Ext JS Library 1.1.1
19847  * Copyright(c) 2006-2007, Ext JS, LLC.
19848  *
19849  * Originally Released Under LGPL - original licence link has changed is not relivant.
19850  *
19851  * Fork - LGPL
19852  * <script type="text/javascript">
19853  */
19854  
19855
19856 /**
19857  * @class Roo.menu.BaseItem
19858  * @extends Roo.Component
19859  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19860  * management and base configuration options shared by all menu components.
19861  * @constructor
19862  * Creates a new BaseItem
19863  * @param {Object} config Configuration options
19864  */
19865 Roo.menu.BaseItem = function(config){
19866     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19867
19868     this.addEvents({
19869         /**
19870          * @event click
19871          * Fires when this item is clicked
19872          * @param {Roo.menu.BaseItem} this
19873          * @param {Roo.EventObject} e
19874          */
19875         click: true,
19876         /**
19877          * @event activate
19878          * Fires when this item is activated
19879          * @param {Roo.menu.BaseItem} this
19880          */
19881         activate : true,
19882         /**
19883          * @event deactivate
19884          * Fires when this item is deactivated
19885          * @param {Roo.menu.BaseItem} this
19886          */
19887         deactivate : true
19888     });
19889
19890     if(this.handler){
19891         this.on("click", this.handler, this.scope, true);
19892     }
19893 };
19894
19895 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19896     /**
19897      * @cfg {Function} handler
19898      * A function that will handle the click event of this menu item (defaults to undefined)
19899      */
19900     /**
19901      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19902      */
19903     canActivate : false,
19904     
19905      /**
19906      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19907      */
19908     hidden: false,
19909     
19910     /**
19911      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19912      */
19913     activeClass : "x-menu-item-active",
19914     /**
19915      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19916      */
19917     hideOnClick : true,
19918     /**
19919      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19920      */
19921     hideDelay : 100,
19922
19923     // private
19924     ctype: "Roo.menu.BaseItem",
19925
19926     // private
19927     actionMode : "container",
19928
19929     // private
19930     render : function(container, parentMenu){
19931         this.parentMenu = parentMenu;
19932         Roo.menu.BaseItem.superclass.render.call(this, container);
19933         this.container.menuItemId = this.id;
19934     },
19935
19936     // private
19937     onRender : function(container, position){
19938         this.el = Roo.get(this.el);
19939         container.dom.appendChild(this.el.dom);
19940     },
19941
19942     // private
19943     onClick : function(e){
19944         if(!this.disabled && this.fireEvent("click", this, e) !== false
19945                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19946             this.handleClick(e);
19947         }else{
19948             e.stopEvent();
19949         }
19950     },
19951
19952     // private
19953     activate : function(){
19954         if(this.disabled){
19955             return false;
19956         }
19957         var li = this.container;
19958         li.addClass(this.activeClass);
19959         this.region = li.getRegion().adjust(2, 2, -2, -2);
19960         this.fireEvent("activate", this);
19961         return true;
19962     },
19963
19964     // private
19965     deactivate : function(){
19966         this.container.removeClass(this.activeClass);
19967         this.fireEvent("deactivate", this);
19968     },
19969
19970     // private
19971     shouldDeactivate : function(e){
19972         return !this.region || !this.region.contains(e.getPoint());
19973     },
19974
19975     // private
19976     handleClick : function(e){
19977         if(this.hideOnClick){
19978             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19979         }
19980     },
19981
19982     // private
19983     expandMenu : function(autoActivate){
19984         // do nothing
19985     },
19986
19987     // private
19988     hideMenu : function(){
19989         // do nothing
19990     }
19991 });/*
19992  * Based on:
19993  * Ext JS Library 1.1.1
19994  * Copyright(c) 2006-2007, Ext JS, LLC.
19995  *
19996  * Originally Released Under LGPL - original licence link has changed is not relivant.
19997  *
19998  * Fork - LGPL
19999  * <script type="text/javascript">
20000  */
20001  
20002 /**
20003  * @class Roo.menu.Adapter
20004  * @extends Roo.menu.BaseItem
20005  * 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.
20006  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20007  * @constructor
20008  * Creates a new Adapter
20009  * @param {Object} config Configuration options
20010  */
20011 Roo.menu.Adapter = function(component, config){
20012     Roo.menu.Adapter.superclass.constructor.call(this, config);
20013     this.component = component;
20014 };
20015 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20016     // private
20017     canActivate : true,
20018
20019     // private
20020     onRender : function(container, position){
20021         this.component.render(container);
20022         this.el = this.component.getEl();
20023     },
20024
20025     // private
20026     activate : function(){
20027         if(this.disabled){
20028             return false;
20029         }
20030         this.component.focus();
20031         this.fireEvent("activate", this);
20032         return true;
20033     },
20034
20035     // private
20036     deactivate : function(){
20037         this.fireEvent("deactivate", this);
20038     },
20039
20040     // private
20041     disable : function(){
20042         this.component.disable();
20043         Roo.menu.Adapter.superclass.disable.call(this);
20044     },
20045
20046     // private
20047     enable : function(){
20048         this.component.enable();
20049         Roo.menu.Adapter.superclass.enable.call(this);
20050     }
20051 });/*
20052  * Based on:
20053  * Ext JS Library 1.1.1
20054  * Copyright(c) 2006-2007, Ext JS, LLC.
20055  *
20056  * Originally Released Under LGPL - original licence link has changed is not relivant.
20057  *
20058  * Fork - LGPL
20059  * <script type="text/javascript">
20060  */
20061
20062 /**
20063  * @class Roo.menu.TextItem
20064  * @extends Roo.menu.BaseItem
20065  * Adds a static text string to a menu, usually used as either a heading or group separator.
20066  * Note: old style constructor with text is still supported.
20067  * 
20068  * @constructor
20069  * Creates a new TextItem
20070  * @param {Object} cfg Configuration
20071  */
20072 Roo.menu.TextItem = function(cfg){
20073     if (typeof(cfg) == 'string') {
20074         this.text = cfg;
20075     } else {
20076         Roo.apply(this,cfg);
20077     }
20078     
20079     Roo.menu.TextItem.superclass.constructor.call(this);
20080 };
20081
20082 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20083     /**
20084      * @cfg {Boolean} text Text to show on item.
20085      */
20086     text : '',
20087     
20088     /**
20089      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20090      */
20091     hideOnClick : false,
20092     /**
20093      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20094      */
20095     itemCls : "x-menu-text",
20096
20097     // private
20098     onRender : function(){
20099         var s = document.createElement("span");
20100         s.className = this.itemCls;
20101         s.innerHTML = this.text;
20102         this.el = s;
20103         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20104     }
20105 });/*
20106  * Based on:
20107  * Ext JS Library 1.1.1
20108  * Copyright(c) 2006-2007, Ext JS, LLC.
20109  *
20110  * Originally Released Under LGPL - original licence link has changed is not relivant.
20111  *
20112  * Fork - LGPL
20113  * <script type="text/javascript">
20114  */
20115
20116 /**
20117  * @class Roo.menu.Separator
20118  * @extends Roo.menu.BaseItem
20119  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20120  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20121  * @constructor
20122  * @param {Object} config Configuration options
20123  */
20124 Roo.menu.Separator = function(config){
20125     Roo.menu.Separator.superclass.constructor.call(this, config);
20126 };
20127
20128 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20129     /**
20130      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20131      */
20132     itemCls : "x-menu-sep",
20133     /**
20134      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20135      */
20136     hideOnClick : false,
20137
20138     // private
20139     onRender : function(li){
20140         var s = document.createElement("span");
20141         s.className = this.itemCls;
20142         s.innerHTML = "&#160;";
20143         this.el = s;
20144         li.addClass("x-menu-sep-li");
20145         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20146     }
20147 });/*
20148  * Based on:
20149  * Ext JS Library 1.1.1
20150  * Copyright(c) 2006-2007, Ext JS, LLC.
20151  *
20152  * Originally Released Under LGPL - original licence link has changed is not relivant.
20153  *
20154  * Fork - LGPL
20155  * <script type="text/javascript">
20156  */
20157 /**
20158  * @class Roo.menu.Item
20159  * @extends Roo.menu.BaseItem
20160  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20161  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20162  * activation and click handling.
20163  * @constructor
20164  * Creates a new Item
20165  * @param {Object} config Configuration options
20166  */
20167 Roo.menu.Item = function(config){
20168     Roo.menu.Item.superclass.constructor.call(this, config);
20169     if(this.menu){
20170         this.menu = Roo.menu.MenuMgr.get(this.menu);
20171     }
20172 };
20173 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20174     
20175     /**
20176      * @cfg {String} text
20177      * The text to show on the menu item.
20178      */
20179     text: '',
20180      /**
20181      * @cfg {String} HTML to render in menu
20182      * The text to show on the menu item (HTML version).
20183      */
20184     html: '',
20185     /**
20186      * @cfg {String} icon
20187      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20188      */
20189     icon: undefined,
20190     /**
20191      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20192      */
20193     itemCls : "x-menu-item",
20194     /**
20195      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20196      */
20197     canActivate : true,
20198     /**
20199      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20200      */
20201     showDelay: 200,
20202     // doc'd in BaseItem
20203     hideDelay: 200,
20204
20205     // private
20206     ctype: "Roo.menu.Item",
20207     
20208     // private
20209     onRender : function(container, position){
20210         var el = document.createElement("a");
20211         el.hideFocus = true;
20212         el.unselectable = "on";
20213         el.href = this.href || "#";
20214         if(this.hrefTarget){
20215             el.target = this.hrefTarget;
20216         }
20217         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20218         
20219         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20220         
20221         el.innerHTML = String.format(
20222                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20223                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20224         this.el = el;
20225         Roo.menu.Item.superclass.onRender.call(this, container, position);
20226     },
20227
20228     /**
20229      * Sets the text to display in this menu item
20230      * @param {String} text The text to display
20231      * @param {Boolean} isHTML true to indicate text is pure html.
20232      */
20233     setText : function(text, isHTML){
20234         if (isHTML) {
20235             this.html = text;
20236         } else {
20237             this.text = text;
20238             this.html = '';
20239         }
20240         if(this.rendered){
20241             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20242      
20243             this.el.update(String.format(
20244                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20245                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20246             this.parentMenu.autoWidth();
20247         }
20248     },
20249
20250     // private
20251     handleClick : function(e){
20252         if(!this.href){ // if no link defined, stop the event automatically
20253             e.stopEvent();
20254         }
20255         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20256     },
20257
20258     // private
20259     activate : function(autoExpand){
20260         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20261             this.focus();
20262             if(autoExpand){
20263                 this.expandMenu();
20264             }
20265         }
20266         return true;
20267     },
20268
20269     // private
20270     shouldDeactivate : function(e){
20271         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20272             if(this.menu && this.menu.isVisible()){
20273                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20274             }
20275             return true;
20276         }
20277         return false;
20278     },
20279
20280     // private
20281     deactivate : function(){
20282         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20283         this.hideMenu();
20284     },
20285
20286     // private
20287     expandMenu : function(autoActivate){
20288         if(!this.disabled && this.menu){
20289             clearTimeout(this.hideTimer);
20290             delete this.hideTimer;
20291             if(!this.menu.isVisible() && !this.showTimer){
20292                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20293             }else if (this.menu.isVisible() && autoActivate){
20294                 this.menu.tryActivate(0, 1);
20295             }
20296         }
20297     },
20298
20299     // private
20300     deferExpand : function(autoActivate){
20301         delete this.showTimer;
20302         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20303         if(autoActivate){
20304             this.menu.tryActivate(0, 1);
20305         }
20306     },
20307
20308     // private
20309     hideMenu : function(){
20310         clearTimeout(this.showTimer);
20311         delete this.showTimer;
20312         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20313             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20314         }
20315     },
20316
20317     // private
20318     deferHide : function(){
20319         delete this.hideTimer;
20320         this.menu.hide();
20321     }
20322 });/*
20323  * Based on:
20324  * Ext JS Library 1.1.1
20325  * Copyright(c) 2006-2007, Ext JS, LLC.
20326  *
20327  * Originally Released Under LGPL - original licence link has changed is not relivant.
20328  *
20329  * Fork - LGPL
20330  * <script type="text/javascript">
20331  */
20332  
20333 /**
20334  * @class Roo.menu.CheckItem
20335  * @extends Roo.menu.Item
20336  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20337  * @constructor
20338  * Creates a new CheckItem
20339  * @param {Object} config Configuration options
20340  */
20341 Roo.menu.CheckItem = function(config){
20342     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20343     this.addEvents({
20344         /**
20345          * @event beforecheckchange
20346          * Fires before the checked value is set, providing an opportunity to cancel if needed
20347          * @param {Roo.menu.CheckItem} this
20348          * @param {Boolean} checked The new checked value that will be set
20349          */
20350         "beforecheckchange" : true,
20351         /**
20352          * @event checkchange
20353          * Fires after the checked value has been set
20354          * @param {Roo.menu.CheckItem} this
20355          * @param {Boolean} checked The checked value that was set
20356          */
20357         "checkchange" : true
20358     });
20359     if(this.checkHandler){
20360         this.on('checkchange', this.checkHandler, this.scope);
20361     }
20362 };
20363 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20364     /**
20365      * @cfg {String} group
20366      * All check items with the same group name will automatically be grouped into a single-select
20367      * radio button group (defaults to '')
20368      */
20369     /**
20370      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20371      */
20372     itemCls : "x-menu-item x-menu-check-item",
20373     /**
20374      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20375      */
20376     groupClass : "x-menu-group-item",
20377
20378     /**
20379      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20380      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20381      * initialized with checked = true will be rendered as checked.
20382      */
20383     checked: false,
20384
20385     // private
20386     ctype: "Roo.menu.CheckItem",
20387
20388     // private
20389     onRender : function(c){
20390         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20391         if(this.group){
20392             this.el.addClass(this.groupClass);
20393         }
20394         Roo.menu.MenuMgr.registerCheckable(this);
20395         if(this.checked){
20396             this.checked = false;
20397             this.setChecked(true, true);
20398         }
20399     },
20400
20401     // private
20402     destroy : function(){
20403         if(this.rendered){
20404             Roo.menu.MenuMgr.unregisterCheckable(this);
20405         }
20406         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20407     },
20408
20409     /**
20410      * Set the checked state of this item
20411      * @param {Boolean} checked The new checked value
20412      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20413      */
20414     setChecked : function(state, suppressEvent){
20415         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20416             if(this.container){
20417                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20418             }
20419             this.checked = state;
20420             if(suppressEvent !== true){
20421                 this.fireEvent("checkchange", this, state);
20422             }
20423         }
20424     },
20425
20426     // private
20427     handleClick : function(e){
20428        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20429            this.setChecked(!this.checked);
20430        }
20431        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20432     }
20433 });/*
20434  * Based on:
20435  * Ext JS Library 1.1.1
20436  * Copyright(c) 2006-2007, Ext JS, LLC.
20437  *
20438  * Originally Released Under LGPL - original licence link has changed is not relivant.
20439  *
20440  * Fork - LGPL
20441  * <script type="text/javascript">
20442  */
20443  
20444 /**
20445  * @class Roo.menu.DateItem
20446  * @extends Roo.menu.Adapter
20447  * A menu item that wraps the {@link Roo.DatPicker} component.
20448  * @constructor
20449  * Creates a new DateItem
20450  * @param {Object} config Configuration options
20451  */
20452 Roo.menu.DateItem = function(config){
20453     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20454     /** The Roo.DatePicker object @type Roo.DatePicker */
20455     this.picker = this.component;
20456     this.addEvents({select: true});
20457     
20458     this.picker.on("render", function(picker){
20459         picker.getEl().swallowEvent("click");
20460         picker.container.addClass("x-menu-date-item");
20461     });
20462
20463     this.picker.on("select", this.onSelect, this);
20464 };
20465
20466 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20467     // private
20468     onSelect : function(picker, date){
20469         this.fireEvent("select", this, date, picker);
20470         Roo.menu.DateItem.superclass.handleClick.call(this);
20471     }
20472 });/*
20473  * Based on:
20474  * Ext JS Library 1.1.1
20475  * Copyright(c) 2006-2007, Ext JS, LLC.
20476  *
20477  * Originally Released Under LGPL - original licence link has changed is not relivant.
20478  *
20479  * Fork - LGPL
20480  * <script type="text/javascript">
20481  */
20482  
20483 /**
20484  * @class Roo.menu.ColorItem
20485  * @extends Roo.menu.Adapter
20486  * A menu item that wraps the {@link Roo.ColorPalette} component.
20487  * @constructor
20488  * Creates a new ColorItem
20489  * @param {Object} config Configuration options
20490  */
20491 Roo.menu.ColorItem = function(config){
20492     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20493     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20494     this.palette = this.component;
20495     this.relayEvents(this.palette, ["select"]);
20496     if(this.selectHandler){
20497         this.on('select', this.selectHandler, this.scope);
20498     }
20499 };
20500 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20501  * Based on:
20502  * Ext JS Library 1.1.1
20503  * Copyright(c) 2006-2007, Ext JS, LLC.
20504  *
20505  * Originally Released Under LGPL - original licence link has changed is not relivant.
20506  *
20507  * Fork - LGPL
20508  * <script type="text/javascript">
20509  */
20510  
20511
20512 /**
20513  * @class Roo.menu.DateMenu
20514  * @extends Roo.menu.Menu
20515  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20516  * @constructor
20517  * Creates a new DateMenu
20518  * @param {Object} config Configuration options
20519  */
20520 Roo.menu.DateMenu = function(config){
20521     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20522     this.plain = true;
20523     var di = new Roo.menu.DateItem(config);
20524     this.add(di);
20525     /**
20526      * The {@link Roo.DatePicker} instance for this DateMenu
20527      * @type DatePicker
20528      */
20529     this.picker = di.picker;
20530     /**
20531      * @event select
20532      * @param {DatePicker} picker
20533      * @param {Date} date
20534      */
20535     this.relayEvents(di, ["select"]);
20536     this.on('beforeshow', function(){
20537         if(this.picker){
20538             this.picker.hideMonthPicker(false);
20539         }
20540     }, this);
20541 };
20542 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20543     cls:'x-date-menu'
20544 });/*
20545  * Based on:
20546  * Ext JS Library 1.1.1
20547  * Copyright(c) 2006-2007, Ext JS, LLC.
20548  *
20549  * Originally Released Under LGPL - original licence link has changed is not relivant.
20550  *
20551  * Fork - LGPL
20552  * <script type="text/javascript">
20553  */
20554  
20555
20556 /**
20557  * @class Roo.menu.ColorMenu
20558  * @extends Roo.menu.Menu
20559  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20560  * @constructor
20561  * Creates a new ColorMenu
20562  * @param {Object} config Configuration options
20563  */
20564 Roo.menu.ColorMenu = function(config){
20565     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20566     this.plain = true;
20567     var ci = new Roo.menu.ColorItem(config);
20568     this.add(ci);
20569     /**
20570      * The {@link Roo.ColorPalette} instance for this ColorMenu
20571      * @type ColorPalette
20572      */
20573     this.palette = ci.palette;
20574     /**
20575      * @event select
20576      * @param {ColorPalette} palette
20577      * @param {String} color
20578      */
20579     this.relayEvents(ci, ["select"]);
20580 };
20581 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20582  * Based on:
20583  * Ext JS Library 1.1.1
20584  * Copyright(c) 2006-2007, Ext JS, LLC.
20585  *
20586  * Originally Released Under LGPL - original licence link has changed is not relivant.
20587  *
20588  * Fork - LGPL
20589  * <script type="text/javascript">
20590  */
20591  
20592 /**
20593  * @class Roo.form.Field
20594  * @extends Roo.BoxComponent
20595  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20596  * @constructor
20597  * Creates a new Field
20598  * @param {Object} config Configuration options
20599  */
20600 Roo.form.Field = function(config){
20601     Roo.form.Field.superclass.constructor.call(this, config);
20602 };
20603
20604 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20605     /**
20606      * @cfg {String} fieldLabel Label to use when rendering a form.
20607      */
20608        /**
20609      * @cfg {String} qtip Mouse over tip
20610      */
20611      
20612     /**
20613      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20614      */
20615     invalidClass : "x-form-invalid",
20616     /**
20617      * @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")
20618      */
20619     invalidText : "The value in this field is invalid",
20620     /**
20621      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20622      */
20623     focusClass : "x-form-focus",
20624     /**
20625      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20626       automatic validation (defaults to "keyup").
20627      */
20628     validationEvent : "keyup",
20629     /**
20630      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20631      */
20632     validateOnBlur : true,
20633     /**
20634      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20635      */
20636     validationDelay : 250,
20637     /**
20638      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20639      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20640      */
20641     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20642     /**
20643      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20644      */
20645     fieldClass : "x-form-field",
20646     /**
20647      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20648      *<pre>
20649 Value         Description
20650 -----------   ----------------------------------------------------------------------
20651 qtip          Display a quick tip when the user hovers over the field
20652 title         Display a default browser title attribute popup
20653 under         Add a block div beneath the field containing the error text
20654 side          Add an error icon to the right of the field with a popup on hover
20655 [element id]  Add the error text directly to the innerHTML of the specified element
20656 </pre>
20657      */
20658     msgTarget : 'qtip',
20659     /**
20660      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20661      */
20662     msgFx : 'normal',
20663
20664     /**
20665      * @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.
20666      */
20667     readOnly : false,
20668
20669     /**
20670      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20671      */
20672     disabled : false,
20673
20674     /**
20675      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20676      */
20677     inputType : undefined,
20678     
20679     /**
20680      * @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).
20681          */
20682         tabIndex : undefined,
20683         
20684     // private
20685     isFormField : true,
20686
20687     // private
20688     hasFocus : false,
20689     /**
20690      * @property {Roo.Element} fieldEl
20691      * Element Containing the rendered Field (with label etc.)
20692      */
20693     /**
20694      * @cfg {Mixed} value A value to initialize this field with.
20695      */
20696     value : undefined,
20697
20698     /**
20699      * @cfg {String} name The field's HTML name attribute.
20700      */
20701     /**
20702      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20703      */
20704
20705         // private ??
20706         initComponent : function(){
20707         Roo.form.Field.superclass.initComponent.call(this);
20708         this.addEvents({
20709             /**
20710              * @event focus
20711              * Fires when this field receives input focus.
20712              * @param {Roo.form.Field} this
20713              */
20714             focus : true,
20715             /**
20716              * @event blur
20717              * Fires when this field loses input focus.
20718              * @param {Roo.form.Field} this
20719              */
20720             blur : true,
20721             /**
20722              * @event specialkey
20723              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20724              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20725              * @param {Roo.form.Field} this
20726              * @param {Roo.EventObject} e The event object
20727              */
20728             specialkey : true,
20729             /**
20730              * @event change
20731              * Fires just before the field blurs if the field value has changed.
20732              * @param {Roo.form.Field} this
20733              * @param {Mixed} newValue The new value
20734              * @param {Mixed} oldValue The original value
20735              */
20736             change : true,
20737             /**
20738              * @event invalid
20739              * Fires after the field has been marked as invalid.
20740              * @param {Roo.form.Field} this
20741              * @param {String} msg The validation message
20742              */
20743             invalid : true,
20744             /**
20745              * @event valid
20746              * Fires after the field has been validated with no errors.
20747              * @param {Roo.form.Field} this
20748              */
20749             valid : true,
20750              /**
20751              * @event keyup
20752              * Fires after the key up
20753              * @param {Roo.form.Field} this
20754              * @param {Roo.EventObject}  e The event Object
20755              */
20756             keyup : true
20757         });
20758     },
20759
20760     /**
20761      * Returns the name attribute of the field if available
20762      * @return {String} name The field name
20763      */
20764     getName: function(){
20765          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20766     },
20767
20768     // private
20769     onRender : function(ct, position){
20770         Roo.form.Field.superclass.onRender.call(this, ct, position);
20771         if(!this.el){
20772             var cfg = this.getAutoCreate();
20773             if(!cfg.name){
20774                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20775             }
20776             if (!cfg.name.length) {
20777                 delete cfg.name;
20778             }
20779             if(this.inputType){
20780                 cfg.type = this.inputType;
20781             }
20782             this.el = ct.createChild(cfg, position);
20783         }
20784         var type = this.el.dom.type;
20785         if(type){
20786             if(type == 'password'){
20787                 type = 'text';
20788             }
20789             this.el.addClass('x-form-'+type);
20790         }
20791         if(this.readOnly){
20792             this.el.dom.readOnly = true;
20793         }
20794         if(this.tabIndex !== undefined){
20795             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20796         }
20797
20798         this.el.addClass([this.fieldClass, this.cls]);
20799         this.initValue();
20800     },
20801
20802     /**
20803      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20804      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20805      * @return {Roo.form.Field} this
20806      */
20807     applyTo : function(target){
20808         this.allowDomMove = false;
20809         this.el = Roo.get(target);
20810         this.render(this.el.dom.parentNode);
20811         return this;
20812     },
20813
20814     // private
20815     initValue : function(){
20816         if(this.value !== undefined){
20817             this.setValue(this.value);
20818         }else if(this.el.dom.value.length > 0){
20819             this.setValue(this.el.dom.value);
20820         }
20821     },
20822
20823     /**
20824      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20825      */
20826     isDirty : function() {
20827         if(this.disabled) {
20828             return false;
20829         }
20830         return String(this.getValue()) !== String(this.originalValue);
20831     },
20832
20833     // private
20834     afterRender : function(){
20835         Roo.form.Field.superclass.afterRender.call(this);
20836         this.initEvents();
20837     },
20838
20839     // private
20840     fireKey : function(e){
20841         //Roo.log('field ' + e.getKey());
20842         if(e.isNavKeyPress()){
20843             this.fireEvent("specialkey", this, e);
20844         }
20845     },
20846
20847     /**
20848      * Resets the current field value to the originally loaded value and clears any validation messages
20849      */
20850     reset : function(){
20851         this.setValue(this.resetValue);
20852         this.clearInvalid();
20853     },
20854
20855     // private
20856     initEvents : function(){
20857         // safari killled keypress - so keydown is now used..
20858         this.el.on("keydown" , this.fireKey,  this);
20859         this.el.on("focus", this.onFocus,  this);
20860         this.el.on("blur", this.onBlur,  this);
20861         this.el.relayEvent('keyup', this);
20862
20863         // reference to original value for reset
20864         this.originalValue = this.getValue();
20865         this.resetValue =  this.getValue();
20866     },
20867
20868     // private
20869     onFocus : function(){
20870         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20871             this.el.addClass(this.focusClass);
20872         }
20873         if(!this.hasFocus){
20874             this.hasFocus = true;
20875             this.startValue = this.getValue();
20876             this.fireEvent("focus", this);
20877         }
20878     },
20879
20880     beforeBlur : Roo.emptyFn,
20881
20882     // private
20883     onBlur : function(){
20884         this.beforeBlur();
20885         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20886             this.el.removeClass(this.focusClass);
20887         }
20888         this.hasFocus = false;
20889         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20890             this.validate();
20891         }
20892         var v = this.getValue();
20893         if(String(v) !== String(this.startValue)){
20894             this.fireEvent('change', this, v, this.startValue);
20895         }
20896         this.fireEvent("blur", this);
20897     },
20898
20899     /**
20900      * Returns whether or not the field value is currently valid
20901      * @param {Boolean} preventMark True to disable marking the field invalid
20902      * @return {Boolean} True if the value is valid, else false
20903      */
20904     isValid : function(preventMark){
20905         if(this.disabled){
20906             return true;
20907         }
20908         var restore = this.preventMark;
20909         this.preventMark = preventMark === true;
20910         var v = this.validateValue(this.processValue(this.getRawValue()));
20911         this.preventMark = restore;
20912         return v;
20913     },
20914
20915     /**
20916      * Validates the field value
20917      * @return {Boolean} True if the value is valid, else false
20918      */
20919     validate : function(){
20920         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20921             this.clearInvalid();
20922             return true;
20923         }
20924         return false;
20925     },
20926
20927     processValue : function(value){
20928         return value;
20929     },
20930
20931     // private
20932     // Subclasses should provide the validation implementation by overriding this
20933     validateValue : function(value){
20934         return true;
20935     },
20936
20937     /**
20938      * Mark this field as invalid
20939      * @param {String} msg The validation message
20940      */
20941     markInvalid : function(msg){
20942         if(!this.rendered || this.preventMark){ // not rendered
20943             return;
20944         }
20945         
20946         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20947         
20948         obj.el.addClass(this.invalidClass);
20949         msg = msg || this.invalidText;
20950         switch(this.msgTarget){
20951             case 'qtip':
20952                 obj.el.dom.qtip = msg;
20953                 obj.el.dom.qclass = 'x-form-invalid-tip';
20954                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20955                     Roo.QuickTips.enable();
20956                 }
20957                 break;
20958             case 'title':
20959                 this.el.dom.title = msg;
20960                 break;
20961             case 'under':
20962                 if(!this.errorEl){
20963                     var elp = this.el.findParent('.x-form-element', 5, true);
20964                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20965                     this.errorEl.setWidth(elp.getWidth(true)-20);
20966                 }
20967                 this.errorEl.update(msg);
20968                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20969                 break;
20970             case 'side':
20971                 if(!this.errorIcon){
20972                     var elp = this.el.findParent('.x-form-element', 5, true);
20973                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20974                 }
20975                 this.alignErrorIcon();
20976                 this.errorIcon.dom.qtip = msg;
20977                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20978                 this.errorIcon.show();
20979                 this.on('resize', this.alignErrorIcon, this);
20980                 break;
20981             default:
20982                 var t = Roo.getDom(this.msgTarget);
20983                 t.innerHTML = msg;
20984                 t.style.display = this.msgDisplay;
20985                 break;
20986         }
20987         this.fireEvent('invalid', this, msg);
20988     },
20989
20990     // private
20991     alignErrorIcon : function(){
20992         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20993     },
20994
20995     /**
20996      * Clear any invalid styles/messages for this field
20997      */
20998     clearInvalid : function(){
20999         if(!this.rendered || this.preventMark){ // not rendered
21000             return;
21001         }
21002         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21003         
21004         obj.el.removeClass(this.invalidClass);
21005         switch(this.msgTarget){
21006             case 'qtip':
21007                 obj.el.dom.qtip = '';
21008                 break;
21009             case 'title':
21010                 this.el.dom.title = '';
21011                 break;
21012             case 'under':
21013                 if(this.errorEl){
21014                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21015                 }
21016                 break;
21017             case 'side':
21018                 if(this.errorIcon){
21019                     this.errorIcon.dom.qtip = '';
21020                     this.errorIcon.hide();
21021                     this.un('resize', this.alignErrorIcon, this);
21022                 }
21023                 break;
21024             default:
21025                 var t = Roo.getDom(this.msgTarget);
21026                 t.innerHTML = '';
21027                 t.style.display = 'none';
21028                 break;
21029         }
21030         this.fireEvent('valid', this);
21031     },
21032
21033     /**
21034      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21035      * @return {Mixed} value The field value
21036      */
21037     getRawValue : function(){
21038         var v = this.el.getValue();
21039         
21040         return v;
21041     },
21042
21043     /**
21044      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21045      * @return {Mixed} value The field value
21046      */
21047     getValue : function(){
21048         var v = this.el.getValue();
21049          
21050         return v;
21051     },
21052
21053     /**
21054      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21055      * @param {Mixed} value The value to set
21056      */
21057     setRawValue : function(v){
21058         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21059     },
21060
21061     /**
21062      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21063      * @param {Mixed} value The value to set
21064      */
21065     setValue : function(v){
21066         this.value = v;
21067         if(this.rendered){
21068             this.el.dom.value = (v === null || v === undefined ? '' : v);
21069              this.validate();
21070         }
21071     },
21072
21073     adjustSize : function(w, h){
21074         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21075         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21076         return s;
21077     },
21078
21079     adjustWidth : function(tag, w){
21080         tag = tag.toLowerCase();
21081         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21082             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21083                 if(tag == 'input'){
21084                     return w + 2;
21085                 }
21086                 if(tag == 'textarea'){
21087                     return w-2;
21088                 }
21089             }else if(Roo.isOpera){
21090                 if(tag == 'input'){
21091                     return w + 2;
21092                 }
21093                 if(tag == 'textarea'){
21094                     return w-2;
21095                 }
21096             }
21097         }
21098         return w;
21099     }
21100 });
21101
21102
21103 // anything other than normal should be considered experimental
21104 Roo.form.Field.msgFx = {
21105     normal : {
21106         show: function(msgEl, f){
21107             msgEl.setDisplayed('block');
21108         },
21109
21110         hide : function(msgEl, f){
21111             msgEl.setDisplayed(false).update('');
21112         }
21113     },
21114
21115     slide : {
21116         show: function(msgEl, f){
21117             msgEl.slideIn('t', {stopFx:true});
21118         },
21119
21120         hide : function(msgEl, f){
21121             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21122         }
21123     },
21124
21125     slideRight : {
21126         show: function(msgEl, f){
21127             msgEl.fixDisplay();
21128             msgEl.alignTo(f.el, 'tl-tr');
21129             msgEl.slideIn('l', {stopFx:true});
21130         },
21131
21132         hide : function(msgEl, f){
21133             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21134         }
21135     }
21136 };/*
21137  * Based on:
21138  * Ext JS Library 1.1.1
21139  * Copyright(c) 2006-2007, Ext JS, LLC.
21140  *
21141  * Originally Released Under LGPL - original licence link has changed is not relivant.
21142  *
21143  * Fork - LGPL
21144  * <script type="text/javascript">
21145  */
21146  
21147
21148 /**
21149  * @class Roo.form.TextField
21150  * @extends Roo.form.Field
21151  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21152  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21153  * @constructor
21154  * Creates a new TextField
21155  * @param {Object} config Configuration options
21156  */
21157 Roo.form.TextField = function(config){
21158     Roo.form.TextField.superclass.constructor.call(this, config);
21159     this.addEvents({
21160         /**
21161          * @event autosize
21162          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21163          * according to the default logic, but this event provides a hook for the developer to apply additional
21164          * logic at runtime to resize the field if needed.
21165              * @param {Roo.form.Field} this This text field
21166              * @param {Number} width The new field width
21167              */
21168         autosize : true
21169     });
21170 };
21171
21172 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21173     /**
21174      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21175      */
21176     grow : false,
21177     /**
21178      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21179      */
21180     growMin : 30,
21181     /**
21182      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21183      */
21184     growMax : 800,
21185     /**
21186      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21187      */
21188     vtype : null,
21189     /**
21190      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21191      */
21192     maskRe : null,
21193     /**
21194      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21195      */
21196     disableKeyFilter : false,
21197     /**
21198      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21199      */
21200     allowBlank : true,
21201     /**
21202      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21203      */
21204     minLength : 0,
21205     /**
21206      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21207      */
21208     maxLength : Number.MAX_VALUE,
21209     /**
21210      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21211      */
21212     minLengthText : "The minimum length for this field is {0}",
21213     /**
21214      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21215      */
21216     maxLengthText : "The maximum length for this field is {0}",
21217     /**
21218      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21219      */
21220     selectOnFocus : false,
21221     /**
21222      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21223      */
21224     blankText : "This field is required",
21225     /**
21226      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21227      * If available, this function will be called only after the basic validators all return true, and will be passed the
21228      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21229      */
21230     validator : null,
21231     /**
21232      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21233      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21234      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21235      */
21236     regex : null,
21237     /**
21238      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21239      */
21240     regexText : "",
21241     /**
21242      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21243      */
21244     emptyText : null,
21245    
21246
21247     // private
21248     initEvents : function()
21249     {
21250         if (this.emptyText) {
21251             this.el.attr('placeholder', this.emptyText);
21252         }
21253         
21254         Roo.form.TextField.superclass.initEvents.call(this);
21255         if(this.validationEvent == 'keyup'){
21256             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21257             this.el.on('keyup', this.filterValidation, this);
21258         }
21259         else if(this.validationEvent !== false){
21260             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21261         }
21262         
21263         if(this.selectOnFocus){
21264             this.on("focus", this.preFocus, this);
21265             
21266         }
21267         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21268             this.el.on("keypress", this.filterKeys, this);
21269         }
21270         if(this.grow){
21271             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21272             this.el.on("click", this.autoSize,  this);
21273         }
21274         if(this.el.is('input[type=password]') && Roo.isSafari){
21275             this.el.on('keydown', this.SafariOnKeyDown, this);
21276         }
21277     },
21278
21279     processValue : function(value){
21280         if(this.stripCharsRe){
21281             var newValue = value.replace(this.stripCharsRe, '');
21282             if(newValue !== value){
21283                 this.setRawValue(newValue);
21284                 return newValue;
21285             }
21286         }
21287         return value;
21288     },
21289
21290     filterValidation : function(e){
21291         if(!e.isNavKeyPress()){
21292             this.validationTask.delay(this.validationDelay);
21293         }
21294     },
21295
21296     // private
21297     onKeyUp : function(e){
21298         if(!e.isNavKeyPress()){
21299             this.autoSize();
21300         }
21301     },
21302
21303     /**
21304      * Resets the current field value to the originally-loaded value and clears any validation messages.
21305      *  
21306      */
21307     reset : function(){
21308         Roo.form.TextField.superclass.reset.call(this);
21309        
21310     },
21311
21312     
21313     // private
21314     preFocus : function(){
21315         
21316         if(this.selectOnFocus){
21317             this.el.dom.select();
21318         }
21319     },
21320
21321     
21322     // private
21323     filterKeys : function(e){
21324         var k = e.getKey();
21325         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21326             return;
21327         }
21328         var c = e.getCharCode(), cc = String.fromCharCode(c);
21329         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21330             return;
21331         }
21332         if(!this.maskRe.test(cc)){
21333             e.stopEvent();
21334         }
21335     },
21336
21337     setValue : function(v){
21338         
21339         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21340         
21341         this.autoSize();
21342     },
21343
21344     /**
21345      * Validates a value according to the field's validation rules and marks the field as invalid
21346      * if the validation fails
21347      * @param {Mixed} value The value to validate
21348      * @return {Boolean} True if the value is valid, else false
21349      */
21350     validateValue : function(value){
21351         if(value.length < 1)  { // if it's blank
21352              if(this.allowBlank){
21353                 this.clearInvalid();
21354                 return true;
21355              }else{
21356                 this.markInvalid(this.blankText);
21357                 return false;
21358              }
21359         }
21360         if(value.length < this.minLength){
21361             this.markInvalid(String.format(this.minLengthText, this.minLength));
21362             return false;
21363         }
21364         if(value.length > this.maxLength){
21365             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21366             return false;
21367         }
21368         if(this.vtype){
21369             var vt = Roo.form.VTypes;
21370             if(!vt[this.vtype](value, this)){
21371                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21372                 return false;
21373             }
21374         }
21375         if(typeof this.validator == "function"){
21376             var msg = this.validator(value);
21377             if(msg !== true){
21378                 this.markInvalid(msg);
21379                 return false;
21380             }
21381         }
21382         if(this.regex && !this.regex.test(value)){
21383             this.markInvalid(this.regexText);
21384             return false;
21385         }
21386         return true;
21387     },
21388
21389     /**
21390      * Selects text in this field
21391      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21392      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21393      */
21394     selectText : function(start, end){
21395         var v = this.getRawValue();
21396         if(v.length > 0){
21397             start = start === undefined ? 0 : start;
21398             end = end === undefined ? v.length : end;
21399             var d = this.el.dom;
21400             if(d.setSelectionRange){
21401                 d.setSelectionRange(start, end);
21402             }else if(d.createTextRange){
21403                 var range = d.createTextRange();
21404                 range.moveStart("character", start);
21405                 range.moveEnd("character", v.length-end);
21406                 range.select();
21407             }
21408         }
21409     },
21410
21411     /**
21412      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21413      * This only takes effect if grow = true, and fires the autosize event.
21414      */
21415     autoSize : function(){
21416         if(!this.grow || !this.rendered){
21417             return;
21418         }
21419         if(!this.metrics){
21420             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21421         }
21422         var el = this.el;
21423         var v = el.dom.value;
21424         var d = document.createElement('div');
21425         d.appendChild(document.createTextNode(v));
21426         v = d.innerHTML;
21427         d = null;
21428         v += "&#160;";
21429         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21430         this.el.setWidth(w);
21431         this.fireEvent("autosize", this, w);
21432     },
21433     
21434     // private
21435     SafariOnKeyDown : function(event)
21436     {
21437         // this is a workaround for a password hang bug on chrome/ webkit.
21438         
21439         var isSelectAll = false;
21440         
21441         if(this.el.dom.selectionEnd > 0){
21442             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21443         }
21444         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21445             event.preventDefault();
21446             this.setValue('');
21447             return;
21448         }
21449         
21450         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21451             
21452             event.preventDefault();
21453             // this is very hacky as keydown always get's upper case.
21454             
21455             var cc = String.fromCharCode(event.getCharCode());
21456             
21457             
21458             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21459             
21460         }
21461         
21462         
21463     }
21464 });/*
21465  * Based on:
21466  * Ext JS Library 1.1.1
21467  * Copyright(c) 2006-2007, Ext JS, LLC.
21468  *
21469  * Originally Released Under LGPL - original licence link has changed is not relivant.
21470  *
21471  * Fork - LGPL
21472  * <script type="text/javascript">
21473  */
21474  
21475 /**
21476  * @class Roo.form.Hidden
21477  * @extends Roo.form.TextField
21478  * Simple Hidden element used on forms 
21479  * 
21480  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21481  * 
21482  * @constructor
21483  * Creates a new Hidden form element.
21484  * @param {Object} config Configuration options
21485  */
21486
21487
21488
21489 // easy hidden field...
21490 Roo.form.Hidden = function(config){
21491     Roo.form.Hidden.superclass.constructor.call(this, config);
21492 };
21493   
21494 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21495     fieldLabel:      '',
21496     inputType:      'hidden',
21497     width:          50,
21498     allowBlank:     true,
21499     labelSeparator: '',
21500     hidden:         true,
21501     itemCls :       'x-form-item-display-none'
21502
21503
21504 });
21505
21506
21507 /*
21508  * Based on:
21509  * Ext JS Library 1.1.1
21510  * Copyright(c) 2006-2007, Ext JS, LLC.
21511  *
21512  * Originally Released Under LGPL - original licence link has changed is not relivant.
21513  *
21514  * Fork - LGPL
21515  * <script type="text/javascript">
21516  */
21517  
21518 /**
21519  * @class Roo.form.TriggerField
21520  * @extends Roo.form.TextField
21521  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21522  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21523  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21524  * for which you can provide a custom implementation.  For example:
21525  * <pre><code>
21526 var trigger = new Roo.form.TriggerField();
21527 trigger.onTriggerClick = myTriggerFn;
21528 trigger.applyTo('my-field');
21529 </code></pre>
21530  *
21531  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21532  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21533  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21534  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21535  * @constructor
21536  * Create a new TriggerField.
21537  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21538  * to the base TextField)
21539  */
21540 Roo.form.TriggerField = function(config){
21541     this.mimicing = false;
21542     Roo.form.TriggerField.superclass.constructor.call(this, config);
21543 };
21544
21545 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21546     /**
21547      * @cfg {String} triggerClass A CSS class to apply to the trigger
21548      */
21549     /**
21550      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21551      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21552      */
21553     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21554     /**
21555      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21556      */
21557     hideTrigger:false,
21558
21559     /** @cfg {Boolean} grow @hide */
21560     /** @cfg {Number} growMin @hide */
21561     /** @cfg {Number} growMax @hide */
21562
21563     /**
21564      * @hide 
21565      * @method
21566      */
21567     autoSize: Roo.emptyFn,
21568     // private
21569     monitorTab : true,
21570     // private
21571     deferHeight : true,
21572
21573     
21574     actionMode : 'wrap',
21575     // private
21576     onResize : function(w, h){
21577         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21578         if(typeof w == 'number'){
21579             var x = w - this.trigger.getWidth();
21580             this.el.setWidth(this.adjustWidth('input', x));
21581             this.trigger.setStyle('left', x+'px');
21582         }
21583     },
21584
21585     // private
21586     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21587
21588     // private
21589     getResizeEl : function(){
21590         return this.wrap;
21591     },
21592
21593     // private
21594     getPositionEl : function(){
21595         return this.wrap;
21596     },
21597
21598     // private
21599     alignErrorIcon : function(){
21600         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21601     },
21602
21603     // private
21604     onRender : function(ct, position){
21605         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21606         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21607         this.trigger = this.wrap.createChild(this.triggerConfig ||
21608                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21609         if(this.hideTrigger){
21610             this.trigger.setDisplayed(false);
21611         }
21612         this.initTrigger();
21613         if(!this.width){
21614             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21615         }
21616     },
21617
21618     // private
21619     initTrigger : function(){
21620         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21621         this.trigger.addClassOnOver('x-form-trigger-over');
21622         this.trigger.addClassOnClick('x-form-trigger-click');
21623     },
21624
21625     // private
21626     onDestroy : function(){
21627         if(this.trigger){
21628             this.trigger.removeAllListeners();
21629             this.trigger.remove();
21630         }
21631         if(this.wrap){
21632             this.wrap.remove();
21633         }
21634         Roo.form.TriggerField.superclass.onDestroy.call(this);
21635     },
21636
21637     // private
21638     onFocus : function(){
21639         Roo.form.TriggerField.superclass.onFocus.call(this);
21640         if(!this.mimicing){
21641             this.wrap.addClass('x-trigger-wrap-focus');
21642             this.mimicing = true;
21643             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21644             if(this.monitorTab){
21645                 this.el.on("keydown", this.checkTab, this);
21646             }
21647         }
21648     },
21649
21650     // private
21651     checkTab : function(e){
21652         if(e.getKey() == e.TAB){
21653             this.triggerBlur();
21654         }
21655     },
21656
21657     // private
21658     onBlur : function(){
21659         // do nothing
21660     },
21661
21662     // private
21663     mimicBlur : function(e, t){
21664         if(!this.wrap.contains(t) && this.validateBlur()){
21665             this.triggerBlur();
21666         }
21667     },
21668
21669     // private
21670     triggerBlur : function(){
21671         this.mimicing = false;
21672         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21673         if(this.monitorTab){
21674             this.el.un("keydown", this.checkTab, this);
21675         }
21676         this.wrap.removeClass('x-trigger-wrap-focus');
21677         Roo.form.TriggerField.superclass.onBlur.call(this);
21678     },
21679
21680     // private
21681     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21682     validateBlur : function(e, t){
21683         return true;
21684     },
21685
21686     // private
21687     onDisable : function(){
21688         Roo.form.TriggerField.superclass.onDisable.call(this);
21689         if(this.wrap){
21690             this.wrap.addClass('x-item-disabled');
21691         }
21692     },
21693
21694     // private
21695     onEnable : function(){
21696         Roo.form.TriggerField.superclass.onEnable.call(this);
21697         if(this.wrap){
21698             this.wrap.removeClass('x-item-disabled');
21699         }
21700     },
21701
21702     // private
21703     onShow : function(){
21704         var ae = this.getActionEl();
21705         
21706         if(ae){
21707             ae.dom.style.display = '';
21708             ae.dom.style.visibility = 'visible';
21709         }
21710     },
21711
21712     // private
21713     
21714     onHide : function(){
21715         var ae = this.getActionEl();
21716         ae.dom.style.display = 'none';
21717     },
21718
21719     /**
21720      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21721      * by an implementing function.
21722      * @method
21723      * @param {EventObject} e
21724      */
21725     onTriggerClick : Roo.emptyFn
21726 });
21727
21728 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21729 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21730 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21731 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21732     initComponent : function(){
21733         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21734
21735         this.triggerConfig = {
21736             tag:'span', cls:'x-form-twin-triggers', cn:[
21737             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21738             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21739         ]};
21740     },
21741
21742     getTrigger : function(index){
21743         return this.triggers[index];
21744     },
21745
21746     initTrigger : function(){
21747         var ts = this.trigger.select('.x-form-trigger', true);
21748         this.wrap.setStyle('overflow', 'hidden');
21749         var triggerField = this;
21750         ts.each(function(t, all, index){
21751             t.hide = function(){
21752                 var w = triggerField.wrap.getWidth();
21753                 this.dom.style.display = 'none';
21754                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21755             };
21756             t.show = function(){
21757                 var w = triggerField.wrap.getWidth();
21758                 this.dom.style.display = '';
21759                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21760             };
21761             var triggerIndex = 'Trigger'+(index+1);
21762
21763             if(this['hide'+triggerIndex]){
21764                 t.dom.style.display = 'none';
21765             }
21766             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21767             t.addClassOnOver('x-form-trigger-over');
21768             t.addClassOnClick('x-form-trigger-click');
21769         }, this);
21770         this.triggers = ts.elements;
21771     },
21772
21773     onTrigger1Click : Roo.emptyFn,
21774     onTrigger2Click : Roo.emptyFn
21775 });/*
21776  * Based on:
21777  * Ext JS Library 1.1.1
21778  * Copyright(c) 2006-2007, Ext JS, LLC.
21779  *
21780  * Originally Released Under LGPL - original licence link has changed is not relivant.
21781  *
21782  * Fork - LGPL
21783  * <script type="text/javascript">
21784  */
21785  
21786 /**
21787  * @class Roo.form.TextArea
21788  * @extends Roo.form.TextField
21789  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21790  * support for auto-sizing.
21791  * @constructor
21792  * Creates a new TextArea
21793  * @param {Object} config Configuration options
21794  */
21795 Roo.form.TextArea = function(config){
21796     Roo.form.TextArea.superclass.constructor.call(this, config);
21797     // these are provided exchanges for backwards compat
21798     // minHeight/maxHeight were replaced by growMin/growMax to be
21799     // compatible with TextField growing config values
21800     if(this.minHeight !== undefined){
21801         this.growMin = this.minHeight;
21802     }
21803     if(this.maxHeight !== undefined){
21804         this.growMax = this.maxHeight;
21805     }
21806 };
21807
21808 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21809     /**
21810      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21811      */
21812     growMin : 60,
21813     /**
21814      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21815      */
21816     growMax: 1000,
21817     /**
21818      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21819      * in the field (equivalent to setting overflow: hidden, defaults to false)
21820      */
21821     preventScrollbars: false,
21822     /**
21823      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21824      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21825      */
21826
21827     // private
21828     onRender : function(ct, position){
21829         if(!this.el){
21830             this.defaultAutoCreate = {
21831                 tag: "textarea",
21832                 style:"width:300px;height:60px;",
21833                 autocomplete: "new-password"
21834             };
21835         }
21836         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21837         if(this.grow){
21838             this.textSizeEl = Roo.DomHelper.append(document.body, {
21839                 tag: "pre", cls: "x-form-grow-sizer"
21840             });
21841             if(this.preventScrollbars){
21842                 this.el.setStyle("overflow", "hidden");
21843             }
21844             this.el.setHeight(this.growMin);
21845         }
21846     },
21847
21848     onDestroy : function(){
21849         if(this.textSizeEl){
21850             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21851         }
21852         Roo.form.TextArea.superclass.onDestroy.call(this);
21853     },
21854
21855     // private
21856     onKeyUp : function(e){
21857         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21858             this.autoSize();
21859         }
21860     },
21861
21862     /**
21863      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21864      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21865      */
21866     autoSize : function(){
21867         if(!this.grow || !this.textSizeEl){
21868             return;
21869         }
21870         var el = this.el;
21871         var v = el.dom.value;
21872         var ts = this.textSizeEl;
21873
21874         ts.innerHTML = '';
21875         ts.appendChild(document.createTextNode(v));
21876         v = ts.innerHTML;
21877
21878         Roo.fly(ts).setWidth(this.el.getWidth());
21879         if(v.length < 1){
21880             v = "&#160;&#160;";
21881         }else{
21882             if(Roo.isIE){
21883                 v = v.replace(/\n/g, '<p>&#160;</p>');
21884             }
21885             v += "&#160;\n&#160;";
21886         }
21887         ts.innerHTML = v;
21888         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21889         if(h != this.lastHeight){
21890             this.lastHeight = h;
21891             this.el.setHeight(h);
21892             this.fireEvent("autosize", this, h);
21893         }
21894     }
21895 });/*
21896  * Based on:
21897  * Ext JS Library 1.1.1
21898  * Copyright(c) 2006-2007, Ext JS, LLC.
21899  *
21900  * Originally Released Under LGPL - original licence link has changed is not relivant.
21901  *
21902  * Fork - LGPL
21903  * <script type="text/javascript">
21904  */
21905  
21906
21907 /**
21908  * @class Roo.form.NumberField
21909  * @extends Roo.form.TextField
21910  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21911  * @constructor
21912  * Creates a new NumberField
21913  * @param {Object} config Configuration options
21914  */
21915 Roo.form.NumberField = function(config){
21916     Roo.form.NumberField.superclass.constructor.call(this, config);
21917 };
21918
21919 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21920     /**
21921      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21922      */
21923     fieldClass: "x-form-field x-form-num-field",
21924     /**
21925      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21926      */
21927     allowDecimals : true,
21928     /**
21929      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21930      */
21931     decimalSeparator : ".",
21932     /**
21933      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21934      */
21935     decimalPrecision : 2,
21936     /**
21937      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21938      */
21939     allowNegative : true,
21940     /**
21941      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21942      */
21943     minValue : Number.NEGATIVE_INFINITY,
21944     /**
21945      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21946      */
21947     maxValue : Number.MAX_VALUE,
21948     /**
21949      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21950      */
21951     minText : "The minimum value for this field is {0}",
21952     /**
21953      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21954      */
21955     maxText : "The maximum value for this field is {0}",
21956     /**
21957      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21958      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21959      */
21960     nanText : "{0} is not a valid number",
21961
21962     // private
21963     initEvents : function(){
21964         Roo.form.NumberField.superclass.initEvents.call(this);
21965         var allowed = "0123456789";
21966         if(this.allowDecimals){
21967             allowed += this.decimalSeparator;
21968         }
21969         if(this.allowNegative){
21970             allowed += "-";
21971         }
21972         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21973         var keyPress = function(e){
21974             var k = e.getKey();
21975             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21976                 return;
21977             }
21978             var c = e.getCharCode();
21979             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21980                 e.stopEvent();
21981             }
21982         };
21983         this.el.on("keypress", keyPress, this);
21984     },
21985
21986     // private
21987     validateValue : function(value){
21988         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21989             return false;
21990         }
21991         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21992              return true;
21993         }
21994         var num = this.parseValue(value);
21995         if(isNaN(num)){
21996             this.markInvalid(String.format(this.nanText, value));
21997             return false;
21998         }
21999         if(num < this.minValue){
22000             this.markInvalid(String.format(this.minText, this.minValue));
22001             return false;
22002         }
22003         if(num > this.maxValue){
22004             this.markInvalid(String.format(this.maxText, this.maxValue));
22005             return false;
22006         }
22007         return true;
22008     },
22009
22010     getValue : function(){
22011         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22012     },
22013
22014     // private
22015     parseValue : function(value){
22016         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22017         return isNaN(value) ? '' : value;
22018     },
22019
22020     // private
22021     fixPrecision : function(value){
22022         var nan = isNaN(value);
22023         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22024             return nan ? '' : value;
22025         }
22026         return parseFloat(value).toFixed(this.decimalPrecision);
22027     },
22028
22029     setValue : function(v){
22030         v = this.fixPrecision(v);
22031         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22032     },
22033
22034     // private
22035     decimalPrecisionFcn : function(v){
22036         return Math.floor(v);
22037     },
22038
22039     beforeBlur : function(){
22040         var v = this.parseValue(this.getRawValue());
22041         if(v){
22042             this.setValue(v);
22043         }
22044     }
22045 });/*
22046  * Based on:
22047  * Ext JS Library 1.1.1
22048  * Copyright(c) 2006-2007, Ext JS, LLC.
22049  *
22050  * Originally Released Under LGPL - original licence link has changed is not relivant.
22051  *
22052  * Fork - LGPL
22053  * <script type="text/javascript">
22054  */
22055  
22056 /**
22057  * @class Roo.form.DateField
22058  * @extends Roo.form.TriggerField
22059  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22060 * @constructor
22061 * Create a new DateField
22062 * @param {Object} config
22063  */
22064 Roo.form.DateField = function(config){
22065     Roo.form.DateField.superclass.constructor.call(this, config);
22066     
22067       this.addEvents({
22068          
22069         /**
22070          * @event select
22071          * Fires when a date is selected
22072              * @param {Roo.form.DateField} combo This combo box
22073              * @param {Date} date The date selected
22074              */
22075         'select' : true
22076          
22077     });
22078     
22079     
22080     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22081     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22082     this.ddMatch = null;
22083     if(this.disabledDates){
22084         var dd = this.disabledDates;
22085         var re = "(?:";
22086         for(var i = 0; i < dd.length; i++){
22087             re += dd[i];
22088             if(i != dd.length-1) re += "|";
22089         }
22090         this.ddMatch = new RegExp(re + ")");
22091     }
22092 };
22093
22094 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22095     /**
22096      * @cfg {String} format
22097      * The default date format string which can be overriden for localization support.  The format must be
22098      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22099      */
22100     format : "m/d/y",
22101     /**
22102      * @cfg {String} altFormats
22103      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22104      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22105      */
22106     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22107     /**
22108      * @cfg {Array} disabledDays
22109      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22110      */
22111     disabledDays : null,
22112     /**
22113      * @cfg {String} disabledDaysText
22114      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22115      */
22116     disabledDaysText : "Disabled",
22117     /**
22118      * @cfg {Array} disabledDates
22119      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22120      * expression so they are very powerful. Some examples:
22121      * <ul>
22122      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22123      * <li>["03/08", "09/16"] would disable those days for every year</li>
22124      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22125      * <li>["03/../2006"] would disable every day in March 2006</li>
22126      * <li>["^03"] would disable every day in every March</li>
22127      * </ul>
22128      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22129      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22130      */
22131     disabledDates : null,
22132     /**
22133      * @cfg {String} disabledDatesText
22134      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22135      */
22136     disabledDatesText : "Disabled",
22137     /**
22138      * @cfg {Date/String} minValue
22139      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22140      * valid format (defaults to null).
22141      */
22142     minValue : null,
22143     /**
22144      * @cfg {Date/String} maxValue
22145      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22146      * valid format (defaults to null).
22147      */
22148     maxValue : null,
22149     /**
22150      * @cfg {String} minText
22151      * The error text to display when the date in the cell is before minValue (defaults to
22152      * 'The date in this field must be after {minValue}').
22153      */
22154     minText : "The date in this field must be equal to or after {0}",
22155     /**
22156      * @cfg {String} maxText
22157      * The error text to display when the date in the cell is after maxValue (defaults to
22158      * 'The date in this field must be before {maxValue}').
22159      */
22160     maxText : "The date in this field must be equal to or before {0}",
22161     /**
22162      * @cfg {String} invalidText
22163      * The error text to display when the date in the field is invalid (defaults to
22164      * '{value} is not a valid date - it must be in the format {format}').
22165      */
22166     invalidText : "{0} is not a valid date - it must be in the format {1}",
22167     /**
22168      * @cfg {String} triggerClass
22169      * An additional CSS class used to style the trigger button.  The trigger will always get the
22170      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22171      * which displays a calendar icon).
22172      */
22173     triggerClass : 'x-form-date-trigger',
22174     
22175
22176     /**
22177      * @cfg {Boolean} useIso
22178      * if enabled, then the date field will use a hidden field to store the 
22179      * real value as iso formated date. default (false)
22180      */ 
22181     useIso : false,
22182     /**
22183      * @cfg {String/Object} autoCreate
22184      * A DomHelper element spec, or true for a default element spec (defaults to
22185      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22186      */ 
22187     // private
22188     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22189     
22190     // private
22191     hiddenField: false,
22192     
22193     onRender : function(ct, position)
22194     {
22195         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22196         if (this.useIso) {
22197             //this.el.dom.removeAttribute('name'); 
22198             Roo.log("Changing name?");
22199             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22200             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22201                     'before', true);
22202             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22203             // prevent input submission
22204             this.hiddenName = this.name;
22205         }
22206             
22207             
22208     },
22209     
22210     // private
22211     validateValue : function(value)
22212     {
22213         value = this.formatDate(value);
22214         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22215             Roo.log('super failed');
22216             return false;
22217         }
22218         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22219              return true;
22220         }
22221         var svalue = value;
22222         value = this.parseDate(value);
22223         if(!value){
22224             Roo.log('parse date failed' + svalue);
22225             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22226             return false;
22227         }
22228         var time = value.getTime();
22229         if(this.minValue && time < this.minValue.getTime()){
22230             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22231             return false;
22232         }
22233         if(this.maxValue && time > this.maxValue.getTime()){
22234             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22235             return false;
22236         }
22237         if(this.disabledDays){
22238             var day = value.getDay();
22239             for(var i = 0; i < this.disabledDays.length; i++) {
22240                 if(day === this.disabledDays[i]){
22241                     this.markInvalid(this.disabledDaysText);
22242                     return false;
22243                 }
22244             }
22245         }
22246         var fvalue = this.formatDate(value);
22247         if(this.ddMatch && this.ddMatch.test(fvalue)){
22248             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22249             return false;
22250         }
22251         return true;
22252     },
22253
22254     // private
22255     // Provides logic to override the default TriggerField.validateBlur which just returns true
22256     validateBlur : function(){
22257         return !this.menu || !this.menu.isVisible();
22258     },
22259     
22260     getName: function()
22261     {
22262         // returns hidden if it's set..
22263         if (!this.rendered) {return ''};
22264         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22265         
22266     },
22267
22268     /**
22269      * Returns the current date value of the date field.
22270      * @return {Date} The date value
22271      */
22272     getValue : function(){
22273         
22274         return  this.hiddenField ?
22275                 this.hiddenField.value :
22276                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22277     },
22278
22279     /**
22280      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22281      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22282      * (the default format used is "m/d/y").
22283      * <br />Usage:
22284      * <pre><code>
22285 //All of these calls set the same date value (May 4, 2006)
22286
22287 //Pass a date object:
22288 var dt = new Date('5/4/06');
22289 dateField.setValue(dt);
22290
22291 //Pass a date string (default format):
22292 dateField.setValue('5/4/06');
22293
22294 //Pass a date string (custom format):
22295 dateField.format = 'Y-m-d';
22296 dateField.setValue('2006-5-4');
22297 </code></pre>
22298      * @param {String/Date} date The date or valid date string
22299      */
22300     setValue : function(date){
22301         if (this.hiddenField) {
22302             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22303         }
22304         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22305         // make sure the value field is always stored as a date..
22306         this.value = this.parseDate(date);
22307         
22308         
22309     },
22310
22311     // private
22312     parseDate : function(value){
22313         if(!value || value instanceof Date){
22314             return value;
22315         }
22316         var v = Date.parseDate(value, this.format);
22317          if (!v && this.useIso) {
22318             v = Date.parseDate(value, 'Y-m-d');
22319         }
22320         if(!v && this.altFormats){
22321             if(!this.altFormatsArray){
22322                 this.altFormatsArray = this.altFormats.split("|");
22323             }
22324             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22325                 v = Date.parseDate(value, this.altFormatsArray[i]);
22326             }
22327         }
22328         return v;
22329     },
22330
22331     // private
22332     formatDate : function(date, fmt){
22333         return (!date || !(date instanceof Date)) ?
22334                date : date.dateFormat(fmt || this.format);
22335     },
22336
22337     // private
22338     menuListeners : {
22339         select: function(m, d){
22340             
22341             this.setValue(d);
22342             this.fireEvent('select', this, d);
22343         },
22344         show : function(){ // retain focus styling
22345             this.onFocus();
22346         },
22347         hide : function(){
22348             this.focus.defer(10, this);
22349             var ml = this.menuListeners;
22350             this.menu.un("select", ml.select,  this);
22351             this.menu.un("show", ml.show,  this);
22352             this.menu.un("hide", ml.hide,  this);
22353         }
22354     },
22355
22356     // private
22357     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22358     onTriggerClick : function(){
22359         if(this.disabled){
22360             return;
22361         }
22362         if(this.menu == null){
22363             this.menu = new Roo.menu.DateMenu();
22364         }
22365         Roo.apply(this.menu.picker,  {
22366             showClear: this.allowBlank,
22367             minDate : this.minValue,
22368             maxDate : this.maxValue,
22369             disabledDatesRE : this.ddMatch,
22370             disabledDatesText : this.disabledDatesText,
22371             disabledDays : this.disabledDays,
22372             disabledDaysText : this.disabledDaysText,
22373             format : this.useIso ? 'Y-m-d' : this.format,
22374             minText : String.format(this.minText, this.formatDate(this.minValue)),
22375             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22376         });
22377         this.menu.on(Roo.apply({}, this.menuListeners, {
22378             scope:this
22379         }));
22380         this.menu.picker.setValue(this.getValue() || new Date());
22381         this.menu.show(this.el, "tl-bl?");
22382     },
22383
22384     beforeBlur : function(){
22385         var v = this.parseDate(this.getRawValue());
22386         if(v){
22387             this.setValue(v);
22388         }
22389     },
22390
22391     /*@
22392      * overide
22393      * 
22394      */
22395     isDirty : function() {
22396         if(this.disabled) {
22397             return false;
22398         }
22399         
22400         if(typeof(this.startValue) === 'undefined'){
22401             return false;
22402         }
22403         
22404         return String(this.getValue()) !== String(this.startValue);
22405         
22406     }
22407 });/*
22408  * Based on:
22409  * Ext JS Library 1.1.1
22410  * Copyright(c) 2006-2007, Ext JS, LLC.
22411  *
22412  * Originally Released Under LGPL - original licence link has changed is not relivant.
22413  *
22414  * Fork - LGPL
22415  * <script type="text/javascript">
22416  */
22417  
22418 /**
22419  * @class Roo.form.MonthField
22420  * @extends Roo.form.TriggerField
22421  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22422 * @constructor
22423 * Create a new MonthField
22424 * @param {Object} config
22425  */
22426 Roo.form.MonthField = function(config){
22427     
22428     Roo.form.MonthField.superclass.constructor.call(this, config);
22429     
22430       this.addEvents({
22431          
22432         /**
22433          * @event select
22434          * Fires when a date is selected
22435              * @param {Roo.form.MonthFieeld} combo This combo box
22436              * @param {Date} date The date selected
22437              */
22438         'select' : true
22439          
22440     });
22441     
22442     
22443     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22444     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22445     this.ddMatch = null;
22446     if(this.disabledDates){
22447         var dd = this.disabledDates;
22448         var re = "(?:";
22449         for(var i = 0; i < dd.length; i++){
22450             re += dd[i];
22451             if(i != dd.length-1) re += "|";
22452         }
22453         this.ddMatch = new RegExp(re + ")");
22454     }
22455 };
22456
22457 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22458     /**
22459      * @cfg {String} format
22460      * The default date format string which can be overriden for localization support.  The format must be
22461      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22462      */
22463     format : "M Y",
22464     /**
22465      * @cfg {String} altFormats
22466      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22467      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22468      */
22469     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22470     /**
22471      * @cfg {Array} disabledDays
22472      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22473      */
22474     disabledDays : [0,1,2,3,4,5,6],
22475     /**
22476      * @cfg {String} disabledDaysText
22477      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22478      */
22479     disabledDaysText : "Disabled",
22480     /**
22481      * @cfg {Array} disabledDates
22482      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22483      * expression so they are very powerful. Some examples:
22484      * <ul>
22485      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22486      * <li>["03/08", "09/16"] would disable those days for every year</li>
22487      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22488      * <li>["03/../2006"] would disable every day in March 2006</li>
22489      * <li>["^03"] would disable every day in every March</li>
22490      * </ul>
22491      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22492      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22493      */
22494     disabledDates : null,
22495     /**
22496      * @cfg {String} disabledDatesText
22497      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22498      */
22499     disabledDatesText : "Disabled",
22500     /**
22501      * @cfg {Date/String} minValue
22502      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22503      * valid format (defaults to null).
22504      */
22505     minValue : null,
22506     /**
22507      * @cfg {Date/String} maxValue
22508      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22509      * valid format (defaults to null).
22510      */
22511     maxValue : null,
22512     /**
22513      * @cfg {String} minText
22514      * The error text to display when the date in the cell is before minValue (defaults to
22515      * 'The date in this field must be after {minValue}').
22516      */
22517     minText : "The date in this field must be equal to or after {0}",
22518     /**
22519      * @cfg {String} maxTextf
22520      * The error text to display when the date in the cell is after maxValue (defaults to
22521      * 'The date in this field must be before {maxValue}').
22522      */
22523     maxText : "The date in this field must be equal to or before {0}",
22524     /**
22525      * @cfg {String} invalidText
22526      * The error text to display when the date in the field is invalid (defaults to
22527      * '{value} is not a valid date - it must be in the format {format}').
22528      */
22529     invalidText : "{0} is not a valid date - it must be in the format {1}",
22530     /**
22531      * @cfg {String} triggerClass
22532      * An additional CSS class used to style the trigger button.  The trigger will always get the
22533      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22534      * which displays a calendar icon).
22535      */
22536     triggerClass : 'x-form-date-trigger',
22537     
22538
22539     /**
22540      * @cfg {Boolean} useIso
22541      * if enabled, then the date field will use a hidden field to store the 
22542      * real value as iso formated date. default (true)
22543      */ 
22544     useIso : true,
22545     /**
22546      * @cfg {String/Object} autoCreate
22547      * A DomHelper element spec, or true for a default element spec (defaults to
22548      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22549      */ 
22550     // private
22551     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22552     
22553     // private
22554     hiddenField: false,
22555     
22556     hideMonthPicker : false,
22557     
22558     onRender : function(ct, position)
22559     {
22560         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22561         if (this.useIso) {
22562             this.el.dom.removeAttribute('name'); 
22563             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22564                     'before', true);
22565             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22566             // prevent input submission
22567             this.hiddenName = this.name;
22568         }
22569             
22570             
22571     },
22572     
22573     // private
22574     validateValue : function(value)
22575     {
22576         value = this.formatDate(value);
22577         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22578             return false;
22579         }
22580         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22581              return true;
22582         }
22583         var svalue = value;
22584         value = this.parseDate(value);
22585         if(!value){
22586             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22587             return false;
22588         }
22589         var time = value.getTime();
22590         if(this.minValue && time < this.minValue.getTime()){
22591             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22592             return false;
22593         }
22594         if(this.maxValue && time > this.maxValue.getTime()){
22595             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22596             return false;
22597         }
22598         /*if(this.disabledDays){
22599             var day = value.getDay();
22600             for(var i = 0; i < this.disabledDays.length; i++) {
22601                 if(day === this.disabledDays[i]){
22602                     this.markInvalid(this.disabledDaysText);
22603                     return false;
22604                 }
22605             }
22606         }
22607         */
22608         var fvalue = this.formatDate(value);
22609         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22610             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22611             return false;
22612         }
22613         */
22614         return true;
22615     },
22616
22617     // private
22618     // Provides logic to override the default TriggerField.validateBlur which just returns true
22619     validateBlur : function(){
22620         return !this.menu || !this.menu.isVisible();
22621     },
22622
22623     /**
22624      * Returns the current date value of the date field.
22625      * @return {Date} The date value
22626      */
22627     getValue : function(){
22628         
22629         
22630         
22631         return  this.hiddenField ?
22632                 this.hiddenField.value :
22633                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22634     },
22635
22636     /**
22637      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22638      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22639      * (the default format used is "m/d/y").
22640      * <br />Usage:
22641      * <pre><code>
22642 //All of these calls set the same date value (May 4, 2006)
22643
22644 //Pass a date object:
22645 var dt = new Date('5/4/06');
22646 monthField.setValue(dt);
22647
22648 //Pass a date string (default format):
22649 monthField.setValue('5/4/06');
22650
22651 //Pass a date string (custom format):
22652 monthField.format = 'Y-m-d';
22653 monthField.setValue('2006-5-4');
22654 </code></pre>
22655      * @param {String/Date} date The date or valid date string
22656      */
22657     setValue : function(date){
22658         Roo.log('month setValue' + date);
22659         // can only be first of month..
22660         
22661         var val = this.parseDate(date);
22662         
22663         if (this.hiddenField) {
22664             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22665         }
22666         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22667         this.value = this.parseDate(date);
22668     },
22669
22670     // private
22671     parseDate : function(value){
22672         if(!value || value instanceof Date){
22673             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22674             return value;
22675         }
22676         var v = Date.parseDate(value, this.format);
22677         if (!v && this.useIso) {
22678             v = Date.parseDate(value, 'Y-m-d');
22679         }
22680         if (v) {
22681             // 
22682             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22683         }
22684         
22685         
22686         if(!v && this.altFormats){
22687             if(!this.altFormatsArray){
22688                 this.altFormatsArray = this.altFormats.split("|");
22689             }
22690             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22691                 v = Date.parseDate(value, this.altFormatsArray[i]);
22692             }
22693         }
22694         return v;
22695     },
22696
22697     // private
22698     formatDate : function(date, fmt){
22699         return (!date || !(date instanceof Date)) ?
22700                date : date.dateFormat(fmt || this.format);
22701     },
22702
22703     // private
22704     menuListeners : {
22705         select: function(m, d){
22706             this.setValue(d);
22707             this.fireEvent('select', this, d);
22708         },
22709         show : function(){ // retain focus styling
22710             this.onFocus();
22711         },
22712         hide : function(){
22713             this.focus.defer(10, this);
22714             var ml = this.menuListeners;
22715             this.menu.un("select", ml.select,  this);
22716             this.menu.un("show", ml.show,  this);
22717             this.menu.un("hide", ml.hide,  this);
22718         }
22719     },
22720     // private
22721     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22722     onTriggerClick : function(){
22723         if(this.disabled){
22724             return;
22725         }
22726         if(this.menu == null){
22727             this.menu = new Roo.menu.DateMenu();
22728            
22729         }
22730         
22731         Roo.apply(this.menu.picker,  {
22732             
22733             showClear: this.allowBlank,
22734             minDate : this.minValue,
22735             maxDate : this.maxValue,
22736             disabledDatesRE : this.ddMatch,
22737             disabledDatesText : this.disabledDatesText,
22738             
22739             format : this.useIso ? 'Y-m-d' : this.format,
22740             minText : String.format(this.minText, this.formatDate(this.minValue)),
22741             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22742             
22743         });
22744          this.menu.on(Roo.apply({}, this.menuListeners, {
22745             scope:this
22746         }));
22747        
22748         
22749         var m = this.menu;
22750         var p = m.picker;
22751         
22752         // hide month picker get's called when we called by 'before hide';
22753         
22754         var ignorehide = true;
22755         p.hideMonthPicker  = function(disableAnim){
22756             if (ignorehide) {
22757                 return;
22758             }
22759              if(this.monthPicker){
22760                 Roo.log("hideMonthPicker called");
22761                 if(disableAnim === true){
22762                     this.monthPicker.hide();
22763                 }else{
22764                     this.monthPicker.slideOut('t', {duration:.2});
22765                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22766                     p.fireEvent("select", this, this.value);
22767                     m.hide();
22768                 }
22769             }
22770         }
22771         
22772         Roo.log('picker set value');
22773         Roo.log(this.getValue());
22774         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22775         m.show(this.el, 'tl-bl?');
22776         ignorehide  = false;
22777         // this will trigger hideMonthPicker..
22778         
22779         
22780         // hidden the day picker
22781         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22782         
22783         
22784         
22785       
22786         
22787         p.showMonthPicker.defer(100, p);
22788     
22789         
22790        
22791     },
22792
22793     beforeBlur : function(){
22794         var v = this.parseDate(this.getRawValue());
22795         if(v){
22796             this.setValue(v);
22797         }
22798     }
22799
22800     /** @cfg {Boolean} grow @hide */
22801     /** @cfg {Number} growMin @hide */
22802     /** @cfg {Number} growMax @hide */
22803     /**
22804      * @hide
22805      * @method autoSize
22806      */
22807 });/*
22808  * Based on:
22809  * Ext JS Library 1.1.1
22810  * Copyright(c) 2006-2007, Ext JS, LLC.
22811  *
22812  * Originally Released Under LGPL - original licence link has changed is not relivant.
22813  *
22814  * Fork - LGPL
22815  * <script type="text/javascript">
22816  */
22817  
22818
22819 /**
22820  * @class Roo.form.ComboBox
22821  * @extends Roo.form.TriggerField
22822  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22823  * @constructor
22824  * Create a new ComboBox.
22825  * @param {Object} config Configuration options
22826  */
22827 Roo.form.ComboBox = function(config){
22828     Roo.form.ComboBox.superclass.constructor.call(this, config);
22829     this.addEvents({
22830         /**
22831          * @event expand
22832          * Fires when the dropdown list is expanded
22833              * @param {Roo.form.ComboBox} combo This combo box
22834              */
22835         'expand' : true,
22836         /**
22837          * @event collapse
22838          * Fires when the dropdown list is collapsed
22839              * @param {Roo.form.ComboBox} combo This combo box
22840              */
22841         'collapse' : true,
22842         /**
22843          * @event beforeselect
22844          * Fires before a list item is selected. Return false to cancel the selection.
22845              * @param {Roo.form.ComboBox} combo This combo box
22846              * @param {Roo.data.Record} record The data record returned from the underlying store
22847              * @param {Number} index The index of the selected item in the dropdown list
22848              */
22849         'beforeselect' : true,
22850         /**
22851          * @event select
22852          * Fires when a list item is selected
22853              * @param {Roo.form.ComboBox} combo This combo box
22854              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22855              * @param {Number} index The index of the selected item in the dropdown list
22856              */
22857         'select' : true,
22858         /**
22859          * @event beforequery
22860          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22861          * The event object passed has these properties:
22862              * @param {Roo.form.ComboBox} combo This combo box
22863              * @param {String} query The query
22864              * @param {Boolean} forceAll true to force "all" query
22865              * @param {Boolean} cancel true to cancel the query
22866              * @param {Object} e The query event object
22867              */
22868         'beforequery': true,
22869          /**
22870          * @event add
22871          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22872              * @param {Roo.form.ComboBox} combo This combo box
22873              */
22874         'add' : true,
22875         /**
22876          * @event edit
22877          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22878              * @param {Roo.form.ComboBox} combo This combo box
22879              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22880              */
22881         'edit' : true
22882         
22883         
22884     });
22885     if(this.transform){
22886         this.allowDomMove = false;
22887         var s = Roo.getDom(this.transform);
22888         if(!this.hiddenName){
22889             this.hiddenName = s.name;
22890         }
22891         if(!this.store){
22892             this.mode = 'local';
22893             var d = [], opts = s.options;
22894             for(var i = 0, len = opts.length;i < len; i++){
22895                 var o = opts[i];
22896                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22897                 if(o.selected) {
22898                     this.value = value;
22899                 }
22900                 d.push([value, o.text]);
22901             }
22902             this.store = new Roo.data.SimpleStore({
22903                 'id': 0,
22904                 fields: ['value', 'text'],
22905                 data : d
22906             });
22907             this.valueField = 'value';
22908             this.displayField = 'text';
22909         }
22910         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22911         if(!this.lazyRender){
22912             this.target = true;
22913             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22914             s.parentNode.removeChild(s); // remove it
22915             this.render(this.el.parentNode);
22916         }else{
22917             s.parentNode.removeChild(s); // remove it
22918         }
22919
22920     }
22921     if (this.store) {
22922         this.store = Roo.factory(this.store, Roo.data);
22923     }
22924     
22925     this.selectedIndex = -1;
22926     if(this.mode == 'local'){
22927         if(config.queryDelay === undefined){
22928             this.queryDelay = 10;
22929         }
22930         if(config.minChars === undefined){
22931             this.minChars = 0;
22932         }
22933     }
22934 };
22935
22936 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22937     /**
22938      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22939      */
22940     /**
22941      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22942      * rendering into an Roo.Editor, defaults to false)
22943      */
22944     /**
22945      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22946      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22947      */
22948     /**
22949      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22950      */
22951     /**
22952      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22953      * the dropdown list (defaults to undefined, with no header element)
22954      */
22955
22956      /**
22957      * @cfg {String/Roo.Template} tpl The template to use to render the output
22958      */
22959      
22960     // private
22961     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22962     /**
22963      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22964      */
22965     listWidth: undefined,
22966     /**
22967      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22968      * mode = 'remote' or 'text' if mode = 'local')
22969      */
22970     displayField: undefined,
22971     /**
22972      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22973      * mode = 'remote' or 'value' if mode = 'local'). 
22974      * Note: use of a valueField requires the user make a selection
22975      * in order for a value to be mapped.
22976      */
22977     valueField: undefined,
22978     
22979     
22980     /**
22981      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22982      * field's data value (defaults to the underlying DOM element's name)
22983      */
22984     hiddenName: undefined,
22985     /**
22986      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22987      */
22988     listClass: '',
22989     /**
22990      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22991      */
22992     selectedClass: 'x-combo-selected',
22993     /**
22994      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22995      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22996      * which displays a downward arrow icon).
22997      */
22998     triggerClass : 'x-form-arrow-trigger',
22999     /**
23000      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23001      */
23002     shadow:'sides',
23003     /**
23004      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23005      * anchor positions (defaults to 'tl-bl')
23006      */
23007     listAlign: 'tl-bl?',
23008     /**
23009      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23010      */
23011     maxHeight: 300,
23012     /**
23013      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23014      * query specified by the allQuery config option (defaults to 'query')
23015      */
23016     triggerAction: 'query',
23017     /**
23018      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23019      * (defaults to 4, does not apply if editable = false)
23020      */
23021     minChars : 4,
23022     /**
23023      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23024      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23025      */
23026     typeAhead: false,
23027     /**
23028      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23029      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23030      */
23031     queryDelay: 500,
23032     /**
23033      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23034      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23035      */
23036     pageSize: 0,
23037     /**
23038      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23039      * when editable = true (defaults to false)
23040      */
23041     selectOnFocus:false,
23042     /**
23043      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23044      */
23045     queryParam: 'query',
23046     /**
23047      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23048      * when mode = 'remote' (defaults to 'Loading...')
23049      */
23050     loadingText: 'Loading...',
23051     /**
23052      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23053      */
23054     resizable: false,
23055     /**
23056      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23057      */
23058     handleHeight : 8,
23059     /**
23060      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23061      * traditional select (defaults to true)
23062      */
23063     editable: true,
23064     /**
23065      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23066      */
23067     allQuery: '',
23068     /**
23069      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23070      */
23071     mode: 'remote',
23072     /**
23073      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23074      * listWidth has a higher value)
23075      */
23076     minListWidth : 70,
23077     /**
23078      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23079      * allow the user to set arbitrary text into the field (defaults to false)
23080      */
23081     forceSelection:false,
23082     /**
23083      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23084      * if typeAhead = true (defaults to 250)
23085      */
23086     typeAheadDelay : 250,
23087     /**
23088      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23089      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23090      */
23091     valueNotFoundText : undefined,
23092     /**
23093      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23094      */
23095     blockFocus : false,
23096     
23097     /**
23098      * @cfg {Boolean} disableClear Disable showing of clear button.
23099      */
23100     disableClear : false,
23101     /**
23102      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23103      */
23104     alwaysQuery : false,
23105     
23106     //private
23107     addicon : false,
23108     editicon: false,
23109     
23110     // element that contains real text value.. (when hidden is used..)
23111      
23112     // private
23113     onRender : function(ct, position){
23114         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23115         if(this.hiddenName){
23116             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23117                     'before', true);
23118             this.hiddenField.value =
23119                 this.hiddenValue !== undefined ? this.hiddenValue :
23120                 this.value !== undefined ? this.value : '';
23121
23122             // prevent input submission
23123             this.el.dom.removeAttribute('name');
23124              
23125              
23126         }
23127         if(Roo.isGecko){
23128             this.el.dom.setAttribute('autocomplete', 'off');
23129         }
23130
23131         var cls = 'x-combo-list';
23132
23133         this.list = new Roo.Layer({
23134             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23135         });
23136
23137         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23138         this.list.setWidth(lw);
23139         this.list.swallowEvent('mousewheel');
23140         this.assetHeight = 0;
23141
23142         if(this.title){
23143             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23144             this.assetHeight += this.header.getHeight();
23145         }
23146
23147         this.innerList = this.list.createChild({cls:cls+'-inner'});
23148         this.innerList.on('mouseover', this.onViewOver, this);
23149         this.innerList.on('mousemove', this.onViewMove, this);
23150         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23151         
23152         if(this.allowBlank && !this.pageSize && !this.disableClear){
23153             this.footer = this.list.createChild({cls:cls+'-ft'});
23154             this.pageTb = new Roo.Toolbar(this.footer);
23155            
23156         }
23157         if(this.pageSize){
23158             this.footer = this.list.createChild({cls:cls+'-ft'});
23159             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23160                     {pageSize: this.pageSize});
23161             
23162         }
23163         
23164         if (this.pageTb && this.allowBlank && !this.disableClear) {
23165             var _this = this;
23166             this.pageTb.add(new Roo.Toolbar.Fill(), {
23167                 cls: 'x-btn-icon x-btn-clear',
23168                 text: '&#160;',
23169                 handler: function()
23170                 {
23171                     _this.collapse();
23172                     _this.clearValue();
23173                     _this.onSelect(false, -1);
23174                 }
23175             });
23176         }
23177         if (this.footer) {
23178             this.assetHeight += this.footer.getHeight();
23179         }
23180         
23181
23182         if(!this.tpl){
23183             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23184         }
23185
23186         this.view = new Roo.View(this.innerList, this.tpl, {
23187             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23188         });
23189
23190         this.view.on('click', this.onViewClick, this);
23191
23192         this.store.on('beforeload', this.onBeforeLoad, this);
23193         this.store.on('load', this.onLoad, this);
23194         this.store.on('loadexception', this.onLoadException, this);
23195
23196         if(this.resizable){
23197             this.resizer = new Roo.Resizable(this.list,  {
23198                pinned:true, handles:'se'
23199             });
23200             this.resizer.on('resize', function(r, w, h){
23201                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23202                 this.listWidth = w;
23203                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23204                 this.restrictHeight();
23205             }, this);
23206             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23207         }
23208         if(!this.editable){
23209             this.editable = true;
23210             this.setEditable(false);
23211         }  
23212         
23213         
23214         if (typeof(this.events.add.listeners) != 'undefined') {
23215             
23216             this.addicon = this.wrap.createChild(
23217                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23218        
23219             this.addicon.on('click', function(e) {
23220                 this.fireEvent('add', this);
23221             }, this);
23222         }
23223         if (typeof(this.events.edit.listeners) != 'undefined') {
23224             
23225             this.editicon = this.wrap.createChild(
23226                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23227             if (this.addicon) {
23228                 this.editicon.setStyle('margin-left', '40px');
23229             }
23230             this.editicon.on('click', function(e) {
23231                 
23232                 // we fire even  if inothing is selected..
23233                 this.fireEvent('edit', this, this.lastData );
23234                 
23235             }, this);
23236         }
23237         
23238         
23239         
23240     },
23241
23242     // private
23243     initEvents : function(){
23244         Roo.form.ComboBox.superclass.initEvents.call(this);
23245
23246         this.keyNav = new Roo.KeyNav(this.el, {
23247             "up" : function(e){
23248                 this.inKeyMode = true;
23249                 this.selectPrev();
23250             },
23251
23252             "down" : function(e){
23253                 if(!this.isExpanded()){
23254                     this.onTriggerClick();
23255                 }else{
23256                     this.inKeyMode = true;
23257                     this.selectNext();
23258                 }
23259             },
23260
23261             "enter" : function(e){
23262                 this.onViewClick();
23263                 //return true;
23264             },
23265
23266             "esc" : function(e){
23267                 this.collapse();
23268             },
23269
23270             "tab" : function(e){
23271                 this.onViewClick(false);
23272                 this.fireEvent("specialkey", this, e);
23273                 return true;
23274             },
23275
23276             scope : this,
23277
23278             doRelay : function(foo, bar, hname){
23279                 if(hname == 'down' || this.scope.isExpanded()){
23280                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23281                 }
23282                 return true;
23283             },
23284
23285             forceKeyDown: true
23286         });
23287         this.queryDelay = Math.max(this.queryDelay || 10,
23288                 this.mode == 'local' ? 10 : 250);
23289         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23290         if(this.typeAhead){
23291             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23292         }
23293         if(this.editable !== false){
23294             this.el.on("keyup", this.onKeyUp, this);
23295         }
23296         if(this.forceSelection){
23297             this.on('blur', this.doForce, this);
23298         }
23299     },
23300
23301     onDestroy : function(){
23302         if(this.view){
23303             this.view.setStore(null);
23304             this.view.el.removeAllListeners();
23305             this.view.el.remove();
23306             this.view.purgeListeners();
23307         }
23308         if(this.list){
23309             this.list.destroy();
23310         }
23311         if(this.store){
23312             this.store.un('beforeload', this.onBeforeLoad, this);
23313             this.store.un('load', this.onLoad, this);
23314             this.store.un('loadexception', this.onLoadException, this);
23315         }
23316         Roo.form.ComboBox.superclass.onDestroy.call(this);
23317     },
23318
23319     // private
23320     fireKey : function(e){
23321         if(e.isNavKeyPress() && !this.list.isVisible()){
23322             this.fireEvent("specialkey", this, e);
23323         }
23324     },
23325
23326     // private
23327     onResize: function(w, h){
23328         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23329         
23330         if(typeof w != 'number'){
23331             // we do not handle it!?!?
23332             return;
23333         }
23334         var tw = this.trigger.getWidth();
23335         tw += this.addicon ? this.addicon.getWidth() : 0;
23336         tw += this.editicon ? this.editicon.getWidth() : 0;
23337         var x = w - tw;
23338         this.el.setWidth( this.adjustWidth('input', x));
23339             
23340         this.trigger.setStyle('left', x+'px');
23341         
23342         if(this.list && this.listWidth === undefined){
23343             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23344             this.list.setWidth(lw);
23345             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23346         }
23347         
23348     
23349         
23350     },
23351
23352     /**
23353      * Allow or prevent the user from directly editing the field text.  If false is passed,
23354      * the user will only be able to select from the items defined in the dropdown list.  This method
23355      * is the runtime equivalent of setting the 'editable' config option at config time.
23356      * @param {Boolean} value True to allow the user to directly edit the field text
23357      */
23358     setEditable : function(value){
23359         if(value == this.editable){
23360             return;
23361         }
23362         this.editable = value;
23363         if(!value){
23364             this.el.dom.setAttribute('readOnly', true);
23365             this.el.on('mousedown', this.onTriggerClick,  this);
23366             this.el.addClass('x-combo-noedit');
23367         }else{
23368             this.el.dom.setAttribute('readOnly', false);
23369             this.el.un('mousedown', this.onTriggerClick,  this);
23370             this.el.removeClass('x-combo-noedit');
23371         }
23372     },
23373
23374     // private
23375     onBeforeLoad : function(){
23376         if(!this.hasFocus){
23377             return;
23378         }
23379         this.innerList.update(this.loadingText ?
23380                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23381         this.restrictHeight();
23382         this.selectedIndex = -1;
23383     },
23384
23385     // private
23386     onLoad : function(){
23387         if(!this.hasFocus){
23388             return;
23389         }
23390         if(this.store.getCount() > 0){
23391             this.expand();
23392             this.restrictHeight();
23393             if(this.lastQuery == this.allQuery){
23394                 if(this.editable){
23395                     this.el.dom.select();
23396                 }
23397                 if(!this.selectByValue(this.value, true)){
23398                     this.select(0, true);
23399                 }
23400             }else{
23401                 this.selectNext();
23402                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23403                     this.taTask.delay(this.typeAheadDelay);
23404                 }
23405             }
23406         }else{
23407             this.onEmptyResults();
23408         }
23409         //this.el.focus();
23410     },
23411     // private
23412     onLoadException : function()
23413     {
23414         this.collapse();
23415         Roo.log(this.store.reader.jsonData);
23416         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23417             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23418         }
23419         
23420         
23421     },
23422     // private
23423     onTypeAhead : function(){
23424         if(this.store.getCount() > 0){
23425             var r = this.store.getAt(0);
23426             var newValue = r.data[this.displayField];
23427             var len = newValue.length;
23428             var selStart = this.getRawValue().length;
23429             if(selStart != len){
23430                 this.setRawValue(newValue);
23431                 this.selectText(selStart, newValue.length);
23432             }
23433         }
23434     },
23435
23436     // private
23437     onSelect : function(record, index){
23438         if(this.fireEvent('beforeselect', this, record, index) !== false){
23439             this.setFromData(index > -1 ? record.data : false);
23440             this.collapse();
23441             this.fireEvent('select', this, record, index);
23442         }
23443     },
23444
23445     /**
23446      * Returns the currently selected field value or empty string if no value is set.
23447      * @return {String} value The selected value
23448      */
23449     getValue : function(){
23450         if(this.valueField){
23451             return typeof this.value != 'undefined' ? this.value : '';
23452         }
23453         return Roo.form.ComboBox.superclass.getValue.call(this);
23454     },
23455
23456     /**
23457      * Clears any text/value currently set in the field
23458      */
23459     clearValue : function(){
23460         if(this.hiddenField){
23461             this.hiddenField.value = '';
23462         }
23463         this.value = '';
23464         this.setRawValue('');
23465         this.lastSelectionText = '';
23466         
23467     },
23468
23469     /**
23470      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23471      * will be displayed in the field.  If the value does not match the data value of an existing item,
23472      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23473      * Otherwise the field will be blank (although the value will still be set).
23474      * @param {String} value The value to match
23475      */
23476     setValue : function(v){
23477         var text = v;
23478         if(this.valueField){
23479             var r = this.findRecord(this.valueField, v);
23480             if(r){
23481                 text = r.data[this.displayField];
23482             }else if(this.valueNotFoundText !== undefined){
23483                 text = this.valueNotFoundText;
23484             }
23485         }
23486         this.lastSelectionText = text;
23487         if(this.hiddenField){
23488             this.hiddenField.value = v;
23489         }
23490         Roo.form.ComboBox.superclass.setValue.call(this, text);
23491         this.value = v;
23492     },
23493     /**
23494      * @property {Object} the last set data for the element
23495      */
23496     
23497     lastData : false,
23498     /**
23499      * Sets the value of the field based on a object which is related to the record format for the store.
23500      * @param {Object} value the value to set as. or false on reset?
23501      */
23502     setFromData : function(o){
23503         var dv = ''; // display value
23504         var vv = ''; // value value..
23505         this.lastData = o;
23506         if (this.displayField) {
23507             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23508         } else {
23509             // this is an error condition!!!
23510             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23511         }
23512         
23513         if(this.valueField){
23514             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23515         }
23516         if(this.hiddenField){
23517             this.hiddenField.value = vv;
23518             
23519             this.lastSelectionText = dv;
23520             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23521             this.value = vv;
23522             return;
23523         }
23524         // no hidden field.. - we store the value in 'value', but still display
23525         // display field!!!!
23526         this.lastSelectionText = dv;
23527         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23528         this.value = vv;
23529         
23530         
23531     },
23532     // private
23533     reset : function(){
23534         // overridden so that last data is reset..
23535         this.setValue(this.resetValue);
23536         this.clearInvalid();
23537         this.lastData = false;
23538         if (this.view) {
23539             this.view.clearSelections();
23540         }
23541     },
23542     // private
23543     findRecord : function(prop, value){
23544         var record;
23545         if(this.store.getCount() > 0){
23546             this.store.each(function(r){
23547                 if(r.data[prop] == value){
23548                     record = r;
23549                     return false;
23550                 }
23551                 return true;
23552             });
23553         }
23554         return record;
23555     },
23556     
23557     getName: function()
23558     {
23559         // returns hidden if it's set..
23560         if (!this.rendered) {return ''};
23561         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23562         
23563     },
23564     // private
23565     onViewMove : function(e, t){
23566         this.inKeyMode = false;
23567     },
23568
23569     // private
23570     onViewOver : function(e, t){
23571         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23572             return;
23573         }
23574         var item = this.view.findItemFromChild(t);
23575         if(item){
23576             var index = this.view.indexOf(item);
23577             this.select(index, false);
23578         }
23579     },
23580
23581     // private
23582     onViewClick : function(doFocus)
23583     {
23584         var index = this.view.getSelectedIndexes()[0];
23585         var r = this.store.getAt(index);
23586         if(r){
23587             this.onSelect(r, index);
23588         }
23589         if(doFocus !== false && !this.blockFocus){
23590             this.el.focus();
23591         }
23592     },
23593
23594     // private
23595     restrictHeight : function(){
23596         this.innerList.dom.style.height = '';
23597         var inner = this.innerList.dom;
23598         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23599         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23600         this.list.beginUpdate();
23601         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23602         this.list.alignTo(this.el, this.listAlign);
23603         this.list.endUpdate();
23604     },
23605
23606     // private
23607     onEmptyResults : function(){
23608         this.collapse();
23609     },
23610
23611     /**
23612      * Returns true if the dropdown list is expanded, else false.
23613      */
23614     isExpanded : function(){
23615         return this.list.isVisible();
23616     },
23617
23618     /**
23619      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23620      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23621      * @param {String} value The data value of the item to select
23622      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23623      * selected item if it is not currently in view (defaults to true)
23624      * @return {Boolean} True if the value matched an item in the list, else false
23625      */
23626     selectByValue : function(v, scrollIntoView){
23627         if(v !== undefined && v !== null){
23628             var r = this.findRecord(this.valueField || this.displayField, v);
23629             if(r){
23630                 this.select(this.store.indexOf(r), scrollIntoView);
23631                 return true;
23632             }
23633         }
23634         return false;
23635     },
23636
23637     /**
23638      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23639      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23640      * @param {Number} index The zero-based index of the list item to select
23641      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23642      * selected item if it is not currently in view (defaults to true)
23643      */
23644     select : function(index, scrollIntoView){
23645         this.selectedIndex = index;
23646         this.view.select(index);
23647         if(scrollIntoView !== false){
23648             var el = this.view.getNode(index);
23649             if(el){
23650                 this.innerList.scrollChildIntoView(el, false);
23651             }
23652         }
23653     },
23654
23655     // private
23656     selectNext : function(){
23657         var ct = this.store.getCount();
23658         if(ct > 0){
23659             if(this.selectedIndex == -1){
23660                 this.select(0);
23661             }else if(this.selectedIndex < ct-1){
23662                 this.select(this.selectedIndex+1);
23663             }
23664         }
23665     },
23666
23667     // private
23668     selectPrev : function(){
23669         var ct = this.store.getCount();
23670         if(ct > 0){
23671             if(this.selectedIndex == -1){
23672                 this.select(0);
23673             }else if(this.selectedIndex != 0){
23674                 this.select(this.selectedIndex-1);
23675             }
23676         }
23677     },
23678
23679     // private
23680     onKeyUp : function(e){
23681         if(this.editable !== false && !e.isSpecialKey()){
23682             this.lastKey = e.getKey();
23683             this.dqTask.delay(this.queryDelay);
23684         }
23685     },
23686
23687     // private
23688     validateBlur : function(){
23689         return !this.list || !this.list.isVisible();   
23690     },
23691
23692     // private
23693     initQuery : function(){
23694         this.doQuery(this.getRawValue());
23695     },
23696
23697     // private
23698     doForce : function(){
23699         if(this.el.dom.value.length > 0){
23700             this.el.dom.value =
23701                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23702              
23703         }
23704     },
23705
23706     /**
23707      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23708      * query allowing the query action to be canceled if needed.
23709      * @param {String} query The SQL query to execute
23710      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23711      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23712      * saved in the current store (defaults to false)
23713      */
23714     doQuery : function(q, forceAll){
23715         if(q === undefined || q === null){
23716             q = '';
23717         }
23718         var qe = {
23719             query: q,
23720             forceAll: forceAll,
23721             combo: this,
23722             cancel:false
23723         };
23724         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23725             return false;
23726         }
23727         q = qe.query;
23728         forceAll = qe.forceAll;
23729         if(forceAll === true || (q.length >= this.minChars)){
23730             if(this.lastQuery != q || this.alwaysQuery){
23731                 this.lastQuery = q;
23732                 if(this.mode == 'local'){
23733                     this.selectedIndex = -1;
23734                     if(forceAll){
23735                         this.store.clearFilter();
23736                     }else{
23737                         this.store.filter(this.displayField, q);
23738                     }
23739                     this.onLoad();
23740                 }else{
23741                     this.store.baseParams[this.queryParam] = q;
23742                     this.store.load({
23743                         params: this.getParams(q)
23744                     });
23745                     this.expand();
23746                 }
23747             }else{
23748                 this.selectedIndex = -1;
23749                 this.onLoad();   
23750             }
23751         }
23752     },
23753
23754     // private
23755     getParams : function(q){
23756         var p = {};
23757         //p[this.queryParam] = q;
23758         if(this.pageSize){
23759             p.start = 0;
23760             p.limit = this.pageSize;
23761         }
23762         return p;
23763     },
23764
23765     /**
23766      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23767      */
23768     collapse : function(){
23769         if(!this.isExpanded()){
23770             return;
23771         }
23772         this.list.hide();
23773         Roo.get(document).un('mousedown', this.collapseIf, this);
23774         Roo.get(document).un('mousewheel', this.collapseIf, this);
23775         if (!this.editable) {
23776             Roo.get(document).un('keydown', this.listKeyPress, this);
23777         }
23778         this.fireEvent('collapse', this);
23779     },
23780
23781     // private
23782     collapseIf : function(e){
23783         if(!e.within(this.wrap) && !e.within(this.list)){
23784             this.collapse();
23785         }
23786     },
23787
23788     /**
23789      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23790      */
23791     expand : function(){
23792         if(this.isExpanded() || !this.hasFocus){
23793             return;
23794         }
23795         this.list.alignTo(this.el, this.listAlign);
23796         this.list.show();
23797         Roo.get(document).on('mousedown', this.collapseIf, this);
23798         Roo.get(document).on('mousewheel', this.collapseIf, this);
23799         if (!this.editable) {
23800             Roo.get(document).on('keydown', this.listKeyPress, this);
23801         }
23802         
23803         this.fireEvent('expand', this);
23804     },
23805
23806     // private
23807     // Implements the default empty TriggerField.onTriggerClick function
23808     onTriggerClick : function(){
23809         if(this.disabled){
23810             return;
23811         }
23812         if(this.isExpanded()){
23813             this.collapse();
23814             if (!this.blockFocus) {
23815                 this.el.focus();
23816             }
23817             
23818         }else {
23819             this.hasFocus = true;
23820             if(this.triggerAction == 'all') {
23821                 this.doQuery(this.allQuery, true);
23822             } else {
23823                 this.doQuery(this.getRawValue());
23824             }
23825             if (!this.blockFocus) {
23826                 this.el.focus();
23827             }
23828         }
23829     },
23830     listKeyPress : function(e)
23831     {
23832         //Roo.log('listkeypress');
23833         // scroll to first matching element based on key pres..
23834         if (e.isSpecialKey()) {
23835             return false;
23836         }
23837         var k = String.fromCharCode(e.getKey()).toUpperCase();
23838         //Roo.log(k);
23839         var match  = false;
23840         var csel = this.view.getSelectedNodes();
23841         var cselitem = false;
23842         if (csel.length) {
23843             var ix = this.view.indexOf(csel[0]);
23844             cselitem  = this.store.getAt(ix);
23845             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23846                 cselitem = false;
23847             }
23848             
23849         }
23850         
23851         this.store.each(function(v) { 
23852             if (cselitem) {
23853                 // start at existing selection.
23854                 if (cselitem.id == v.id) {
23855                     cselitem = false;
23856                 }
23857                 return;
23858             }
23859                 
23860             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23861                 match = this.store.indexOf(v);
23862                 return false;
23863             }
23864         }, this);
23865         
23866         if (match === false) {
23867             return true; // no more action?
23868         }
23869         // scroll to?
23870         this.view.select(match);
23871         var sn = Roo.get(this.view.getSelectedNodes()[0])
23872         sn.scrollIntoView(sn.dom.parentNode, false);
23873     }
23874
23875     /** 
23876     * @cfg {Boolean} grow 
23877     * @hide 
23878     */
23879     /** 
23880     * @cfg {Number} growMin 
23881     * @hide 
23882     */
23883     /** 
23884     * @cfg {Number} growMax 
23885     * @hide 
23886     */
23887     /**
23888      * @hide
23889      * @method autoSize
23890      */
23891 });/*
23892  * Copyright(c) 2010-2012, Roo J Solutions Limited
23893  *
23894  * Licence LGPL
23895  *
23896  */
23897
23898 /**
23899  * @class Roo.form.ComboBoxArray
23900  * @extends Roo.form.TextField
23901  * A facebook style adder... for lists of email / people / countries  etc...
23902  * pick multiple items from a combo box, and shows each one.
23903  *
23904  *  Fred [x]  Brian [x]  [Pick another |v]
23905  *
23906  *
23907  *  For this to work: it needs various extra information
23908  *    - normal combo problay has
23909  *      name, hiddenName
23910  *    + displayField, valueField
23911  *
23912  *    For our purpose...
23913  *
23914  *
23915  *   If we change from 'extends' to wrapping...
23916  *   
23917  *  
23918  *
23919  
23920  
23921  * @constructor
23922  * Create a new ComboBoxArray.
23923  * @param {Object} config Configuration options
23924  */
23925  
23926
23927 Roo.form.ComboBoxArray = function(config)
23928 {
23929     this.addEvents({
23930         /**
23931          * @event beforeremove
23932          * Fires before remove the value from the list
23933              * @param {Roo.form.ComboBoxArray} _self This combo box array
23934              * @param {Roo.form.ComboBoxArray.Item} item removed item
23935              */
23936         'beforeremove' : true,
23937         /**
23938          * @event remove
23939          * Fires when remove the value from the list
23940              * @param {Roo.form.ComboBoxArray} _self This combo box array
23941              * @param {Roo.form.ComboBoxArray.Item} item removed item
23942              */
23943         'remove' : true
23944         
23945         
23946     });
23947     
23948     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23949     
23950     this.items = new Roo.util.MixedCollection(false);
23951     
23952     // construct the child combo...
23953     
23954     
23955     
23956     
23957    
23958     
23959 }
23960
23961  
23962 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23963
23964     /**
23965      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23966      */
23967     
23968     lastData : false,
23969     
23970     // behavies liek a hiddne field
23971     inputType:      'hidden',
23972     /**
23973      * @cfg {Number} width The width of the box that displays the selected element
23974      */ 
23975     width:          300,
23976
23977     
23978     
23979     /**
23980      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23981      */
23982     name : false,
23983     /**
23984      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23985      */
23986     hiddenName : false,
23987     
23988     
23989     // private the array of items that are displayed..
23990     items  : false,
23991     // private - the hidden field el.
23992     hiddenEl : false,
23993     // private - the filed el..
23994     el : false,
23995     
23996     //validateValue : function() { return true; }, // all values are ok!
23997     //onAddClick: function() { },
23998     
23999     onRender : function(ct, position) 
24000     {
24001         
24002         // create the standard hidden element
24003         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24004         
24005         
24006         // give fake names to child combo;
24007         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24008         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24009         
24010         this.combo = Roo.factory(this.combo, Roo.form);
24011         this.combo.onRender(ct, position);
24012         if (typeof(this.combo.width) != 'undefined') {
24013             this.combo.onResize(this.combo.width,0);
24014         }
24015         
24016         this.combo.initEvents();
24017         
24018         // assigned so form know we need to do this..
24019         this.store          = this.combo.store;
24020         this.valueField     = this.combo.valueField;
24021         this.displayField   = this.combo.displayField ;
24022         
24023         
24024         this.combo.wrap.addClass('x-cbarray-grp');
24025         
24026         var cbwrap = this.combo.wrap.createChild(
24027             {tag: 'div', cls: 'x-cbarray-cb'},
24028             this.combo.el.dom
24029         );
24030         
24031              
24032         this.hiddenEl = this.combo.wrap.createChild({
24033             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24034         });
24035         this.el = this.combo.wrap.createChild({
24036             tag: 'input',  type:'hidden' , name: this.name, value : ''
24037         });
24038          //   this.el.dom.removeAttribute("name");
24039         
24040         
24041         this.outerWrap = this.combo.wrap;
24042         this.wrap = cbwrap;
24043         
24044         this.outerWrap.setWidth(this.width);
24045         this.outerWrap.dom.removeChild(this.el.dom);
24046         
24047         this.wrap.dom.appendChild(this.el.dom);
24048         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24049         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24050         
24051         this.combo.trigger.setStyle('position','relative');
24052         this.combo.trigger.setStyle('left', '0px');
24053         this.combo.trigger.setStyle('top', '2px');
24054         
24055         this.combo.el.setStyle('vertical-align', 'text-bottom');
24056         
24057         //this.trigger.setStyle('vertical-align', 'top');
24058         
24059         // this should use the code from combo really... on('add' ....)
24060         if (this.adder) {
24061             
24062         
24063             this.adder = this.outerWrap.createChild(
24064                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24065             var _t = this;
24066             this.adder.on('click', function(e) {
24067                 _t.fireEvent('adderclick', this, e);
24068             }, _t);
24069         }
24070         //var _t = this;
24071         //this.adder.on('click', this.onAddClick, _t);
24072         
24073         
24074         this.combo.on('select', function(cb, rec, ix) {
24075             this.addItem(rec.data);
24076             
24077             cb.setValue('');
24078             cb.el.dom.value = '';
24079             //cb.lastData = rec.data;
24080             // add to list
24081             
24082         }, this);
24083         
24084         
24085     },
24086     
24087     
24088     getName: function()
24089     {
24090         // returns hidden if it's set..
24091         if (!this.rendered) {return ''};
24092         return  this.hiddenName ? this.hiddenName : this.name;
24093         
24094     },
24095     
24096     
24097     onResize: function(w, h){
24098         
24099         return;
24100         // not sure if this is needed..
24101         //this.combo.onResize(w,h);
24102         
24103         if(typeof w != 'number'){
24104             // we do not handle it!?!?
24105             return;
24106         }
24107         var tw = this.combo.trigger.getWidth();
24108         tw += this.addicon ? this.addicon.getWidth() : 0;
24109         tw += this.editicon ? this.editicon.getWidth() : 0;
24110         var x = w - tw;
24111         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24112             
24113         this.combo.trigger.setStyle('left', '0px');
24114         
24115         if(this.list && this.listWidth === undefined){
24116             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24117             this.list.setWidth(lw);
24118             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24119         }
24120         
24121     
24122         
24123     },
24124     
24125     addItem: function(rec)
24126     {
24127         var valueField = this.combo.valueField;
24128         var displayField = this.combo.displayField;
24129         if (this.items.indexOfKey(rec[valueField]) > -1) {
24130             //console.log("GOT " + rec.data.id);
24131             return;
24132         }
24133         
24134         var x = new Roo.form.ComboBoxArray.Item({
24135             //id : rec[this.idField],
24136             data : rec,
24137             displayField : displayField ,
24138             tipField : displayField ,
24139             cb : this
24140         });
24141         // use the 
24142         this.items.add(rec[valueField],x);
24143         // add it before the element..
24144         this.updateHiddenEl();
24145         x.render(this.outerWrap, this.wrap.dom);
24146         // add the image handler..
24147     },
24148     
24149     updateHiddenEl : function()
24150     {
24151         this.validate();
24152         if (!this.hiddenEl) {
24153             return;
24154         }
24155         var ar = [];
24156         var idField = this.combo.valueField;
24157         
24158         this.items.each(function(f) {
24159             ar.push(f.data[idField]);
24160            
24161         });
24162         this.hiddenEl.dom.value = ar.join(',');
24163         this.validate();
24164     },
24165     
24166     reset : function()
24167     {
24168         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24169         this.items.each(function(f) {
24170            f.remove(); 
24171         });
24172         this.el.dom.value = '';
24173         if (this.hiddenEl) {
24174             this.hiddenEl.dom.value = '';
24175         }
24176         
24177     },
24178     getValue: function()
24179     {
24180         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24181     },
24182     setValue: function(v) // not a valid action - must use addItems..
24183     {
24184          
24185         this.reset();
24186         
24187         
24188         
24189         if (this.store.isLocal && (typeof(v) == 'string')) {
24190             // then we can use the store to find the values..
24191             // comma seperated at present.. this needs to allow JSON based encoding..
24192             this.hiddenEl.value  = v;
24193             var v_ar = [];
24194             Roo.each(v.split(','), function(k) {
24195                 Roo.log("CHECK " + this.valueField + ',' + k);
24196                 var li = this.store.query(this.valueField, k);
24197                 if (!li.length) {
24198                     return;
24199                 }
24200                 var add = {};
24201                 add[this.valueField] = k;
24202                 add[this.displayField] = li.item(0).data[this.displayField];
24203                 
24204                 this.addItem(add);
24205             }, this) 
24206              
24207         }
24208         if (typeof(v) == 'object' ) {
24209             // then let's assume it's an array of objects..
24210             Roo.each(v, function(l) {
24211                 this.addItem(l);
24212             }, this);
24213              
24214         }
24215         
24216         
24217     },
24218     setFromData: function(v)
24219     {
24220         // this recieves an object, if setValues is called.
24221         this.reset();
24222         this.el.dom.value = v[this.displayField];
24223         this.hiddenEl.dom.value = v[this.valueField];
24224         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24225             return;
24226         }
24227         var kv = v[this.valueField];
24228         var dv = v[this.displayField];
24229         kv = typeof(kv) != 'string' ? '' : kv;
24230         dv = typeof(dv) != 'string' ? '' : dv;
24231         
24232         
24233         var keys = kv.split(',');
24234         var display = dv.split(',');
24235         for (var i = 0 ; i < keys.length; i++) {
24236             
24237             add = {};
24238             add[this.valueField] = keys[i];
24239             add[this.displayField] = display[i];
24240             this.addItem(add);
24241         }
24242       
24243         
24244     },
24245     
24246     /**
24247      * Validates the combox array value
24248      * @return {Boolean} True if the value is valid, else false
24249      */
24250     validate : function(){
24251         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24252             this.clearInvalid();
24253             return true;
24254         }
24255         return false;
24256     },
24257     
24258     validateValue : function(value){
24259         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24260         
24261     },
24262     
24263     /*@
24264      * overide
24265      * 
24266      */
24267     isDirty : function() {
24268         if(this.disabled) {
24269             return false;
24270         }
24271         
24272         try {
24273             var d = Roo.decode(String(this.originalValue));
24274         } catch (e) {
24275             return String(this.getValue()) !== String(this.originalValue);
24276         }
24277         
24278         var originalValue = [];
24279         
24280         for (var i = 0; i < d.length; i++){
24281             originalValue.push(d[i][this.valueField]);
24282         }
24283         
24284         return String(this.getValue()) !== String(originalValue.join(','));
24285         
24286     }
24287     
24288 });
24289
24290
24291
24292 /**
24293  * @class Roo.form.ComboBoxArray.Item
24294  * @extends Roo.BoxComponent
24295  * A selected item in the list
24296  *  Fred [x]  Brian [x]  [Pick another |v]
24297  * 
24298  * @constructor
24299  * Create a new item.
24300  * @param {Object} config Configuration options
24301  */
24302  
24303 Roo.form.ComboBoxArray.Item = function(config) {
24304     config.id = Roo.id();
24305     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24306 }
24307
24308 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24309     data : {},
24310     cb: false,
24311     displayField : false,
24312     tipField : false,
24313     
24314     
24315     defaultAutoCreate : {
24316         tag: 'div',
24317         cls: 'x-cbarray-item',
24318         cn : [ 
24319             { tag: 'div' },
24320             {
24321                 tag: 'img',
24322                 width:16,
24323                 height : 16,
24324                 src : Roo.BLANK_IMAGE_URL ,
24325                 align: 'center'
24326             }
24327         ]
24328         
24329     },
24330     
24331  
24332     onRender : function(ct, position)
24333     {
24334         Roo.form.Field.superclass.onRender.call(this, ct, position);
24335         
24336         if(!this.el){
24337             var cfg = this.getAutoCreate();
24338             this.el = ct.createChild(cfg, position);
24339         }
24340         
24341         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24342         
24343         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24344             this.cb.renderer(this.data) :
24345             String.format('{0}',this.data[this.displayField]);
24346         
24347             
24348         this.el.child('div').dom.setAttribute('qtip',
24349                         String.format('{0}',this.data[this.tipField])
24350         );
24351         
24352         this.el.child('img').on('click', this.remove, this);
24353         
24354     },
24355    
24356     remove : function()
24357     {
24358         if(this.cb.disabled){
24359             return;
24360         }
24361         
24362         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24363             this.cb.items.remove(this);
24364             this.el.child('img').un('click', this.remove, this);
24365             this.el.remove();
24366             this.cb.updateHiddenEl();
24367
24368             this.cb.fireEvent('remove', this.cb, this);
24369         }
24370         
24371     }
24372 });/*
24373  * Based on:
24374  * Ext JS Library 1.1.1
24375  * Copyright(c) 2006-2007, Ext JS, LLC.
24376  *
24377  * Originally Released Under LGPL - original licence link has changed is not relivant.
24378  *
24379  * Fork - LGPL
24380  * <script type="text/javascript">
24381  */
24382 /**
24383  * @class Roo.form.Checkbox
24384  * @extends Roo.form.Field
24385  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24386  * @constructor
24387  * Creates a new Checkbox
24388  * @param {Object} config Configuration options
24389  */
24390 Roo.form.Checkbox = function(config){
24391     Roo.form.Checkbox.superclass.constructor.call(this, config);
24392     this.addEvents({
24393         /**
24394          * @event check
24395          * Fires when the checkbox is checked or unchecked.
24396              * @param {Roo.form.Checkbox} this This checkbox
24397              * @param {Boolean} checked The new checked value
24398              */
24399         check : true
24400     });
24401 };
24402
24403 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24404     /**
24405      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24406      */
24407     focusClass : undefined,
24408     /**
24409      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24410      */
24411     fieldClass: "x-form-field",
24412     /**
24413      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24414      */
24415     checked: false,
24416     /**
24417      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24418      * {tag: "input", type: "checkbox", autocomplete: "off"})
24419      */
24420     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24421     /**
24422      * @cfg {String} boxLabel The text that appears beside the checkbox
24423      */
24424     boxLabel : "",
24425     /**
24426      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24427      */  
24428     inputValue : '1',
24429     /**
24430      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24431      */
24432      valueOff: '0', // value when not checked..
24433
24434     actionMode : 'viewEl', 
24435     //
24436     // private
24437     itemCls : 'x-menu-check-item x-form-item',
24438     groupClass : 'x-menu-group-item',
24439     inputType : 'hidden',
24440     
24441     
24442     inSetChecked: false, // check that we are not calling self...
24443     
24444     inputElement: false, // real input element?
24445     basedOn: false, // ????
24446     
24447     isFormField: true, // not sure where this is needed!!!!
24448
24449     onResize : function(){
24450         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24451         if(!this.boxLabel){
24452             this.el.alignTo(this.wrap, 'c-c');
24453         }
24454     },
24455
24456     initEvents : function(){
24457         Roo.form.Checkbox.superclass.initEvents.call(this);
24458         this.el.on("click", this.onClick,  this);
24459         this.el.on("change", this.onClick,  this);
24460     },
24461
24462
24463     getResizeEl : function(){
24464         return this.wrap;
24465     },
24466
24467     getPositionEl : function(){
24468         return this.wrap;
24469     },
24470
24471     // private
24472     onRender : function(ct, position){
24473         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24474         /*
24475         if(this.inputValue !== undefined){
24476             this.el.dom.value = this.inputValue;
24477         }
24478         */
24479         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24480         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24481         var viewEl = this.wrap.createChild({ 
24482             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24483         this.viewEl = viewEl;   
24484         this.wrap.on('click', this.onClick,  this); 
24485         
24486         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24487         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24488         
24489         
24490         
24491         if(this.boxLabel){
24492             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24493         //    viewEl.on('click', this.onClick,  this); 
24494         }
24495         //if(this.checked){
24496             this.setChecked(this.checked);
24497         //}else{
24498             //this.checked = this.el.dom;
24499         //}
24500
24501     },
24502
24503     // private
24504     initValue : Roo.emptyFn,
24505
24506     /**
24507      * Returns the checked state of the checkbox.
24508      * @return {Boolean} True if checked, else false
24509      */
24510     getValue : function(){
24511         if(this.el){
24512             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24513         }
24514         return this.valueOff;
24515         
24516     },
24517
24518         // private
24519     onClick : function(){ 
24520         if (this.disabled) {
24521             return;
24522         }
24523         this.setChecked(!this.checked);
24524
24525         //if(this.el.dom.checked != this.checked){
24526         //    this.setValue(this.el.dom.checked);
24527        // }
24528     },
24529
24530     /**
24531      * Sets the checked state of the checkbox.
24532      * On is always based on a string comparison between inputValue and the param.
24533      * @param {Boolean/String} value - the value to set 
24534      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24535      */
24536     setValue : function(v,suppressEvent){
24537         
24538         
24539         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24540         //if(this.el && this.el.dom){
24541         //    this.el.dom.checked = this.checked;
24542         //    this.el.dom.defaultChecked = this.checked;
24543         //}
24544         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24545         //this.fireEvent("check", this, this.checked);
24546     },
24547     // private..
24548     setChecked : function(state,suppressEvent)
24549     {
24550         if (this.inSetChecked) {
24551             this.checked = state;
24552             return;
24553         }
24554         
24555     
24556         if(this.wrap){
24557             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24558         }
24559         this.checked = state;
24560         if(suppressEvent !== true){
24561             this.fireEvent('check', this, state);
24562         }
24563         this.inSetChecked = true;
24564         this.el.dom.value = state ? this.inputValue : this.valueOff;
24565         this.inSetChecked = false;
24566         
24567     },
24568     // handle setting of hidden value by some other method!!?!?
24569     setFromHidden: function()
24570     {
24571         if(!this.el){
24572             return;
24573         }
24574         //console.log("SET FROM HIDDEN");
24575         //alert('setFrom hidden');
24576         this.setValue(this.el.dom.value);
24577     },
24578     
24579     onDestroy : function()
24580     {
24581         if(this.viewEl){
24582             Roo.get(this.viewEl).remove();
24583         }
24584          
24585         Roo.form.Checkbox.superclass.onDestroy.call(this);
24586     }
24587
24588 });/*
24589  * Based on:
24590  * Ext JS Library 1.1.1
24591  * Copyright(c) 2006-2007, Ext JS, LLC.
24592  *
24593  * Originally Released Under LGPL - original licence link has changed is not relivant.
24594  *
24595  * Fork - LGPL
24596  * <script type="text/javascript">
24597  */
24598  
24599 /**
24600  * @class Roo.form.Radio
24601  * @extends Roo.form.Checkbox
24602  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24603  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24604  * @constructor
24605  * Creates a new Radio
24606  * @param {Object} config Configuration options
24607  */
24608 Roo.form.Radio = function(){
24609     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24610 };
24611 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24612     inputType: 'radio',
24613
24614     /**
24615      * If this radio is part of a group, it will return the selected value
24616      * @return {String}
24617      */
24618     getGroupValue : function(){
24619         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24620     },
24621     
24622     
24623     onRender : function(ct, position){
24624         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24625         
24626         if(this.inputValue !== undefined){
24627             this.el.dom.value = this.inputValue;
24628         }
24629          
24630         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24631         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24632         //var viewEl = this.wrap.createChild({ 
24633         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24634         //this.viewEl = viewEl;   
24635         //this.wrap.on('click', this.onClick,  this); 
24636         
24637         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24638         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24639         
24640         
24641         
24642         if(this.boxLabel){
24643             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24644         //    viewEl.on('click', this.onClick,  this); 
24645         }
24646          if(this.checked){
24647             this.el.dom.checked =   'checked' ;
24648         }
24649          
24650     } 
24651     
24652     
24653 });//<script type="text/javascript">
24654
24655 /*
24656  * Based  Ext JS Library 1.1.1
24657  * Copyright(c) 2006-2007, Ext JS, LLC.
24658  * LGPL
24659  *
24660  */
24661  
24662 /**
24663  * @class Roo.HtmlEditorCore
24664  * @extends Roo.Component
24665  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24666  *
24667  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24668  */
24669
24670 Roo.HtmlEditorCore = function(config){
24671     
24672     
24673     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24674     
24675     
24676     this.addEvents({
24677         /**
24678          * @event initialize
24679          * Fires when the editor is fully initialized (including the iframe)
24680          * @param {Roo.HtmlEditorCore} this
24681          */
24682         initialize: true,
24683         /**
24684          * @event activate
24685          * Fires when the editor is first receives the focus. Any insertion must wait
24686          * until after this event.
24687          * @param {Roo.HtmlEditorCore} this
24688          */
24689         activate: true,
24690          /**
24691          * @event beforesync
24692          * Fires before the textarea is updated with content from the editor iframe. Return false
24693          * to cancel the sync.
24694          * @param {Roo.HtmlEditorCore} this
24695          * @param {String} html
24696          */
24697         beforesync: true,
24698          /**
24699          * @event beforepush
24700          * Fires before the iframe editor is updated with content from the textarea. Return false
24701          * to cancel the push.
24702          * @param {Roo.HtmlEditorCore} this
24703          * @param {String} html
24704          */
24705         beforepush: true,
24706          /**
24707          * @event sync
24708          * Fires when the textarea is updated with content from the editor iframe.
24709          * @param {Roo.HtmlEditorCore} this
24710          * @param {String} html
24711          */
24712         sync: true,
24713          /**
24714          * @event push
24715          * Fires when the iframe editor is updated with content from the textarea.
24716          * @param {Roo.HtmlEditorCore} this
24717          * @param {String} html
24718          */
24719         push: true,
24720         
24721         /**
24722          * @event editorevent
24723          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24724          * @param {Roo.HtmlEditorCore} this
24725          */
24726         editorevent: true
24727         
24728     });
24729     
24730     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24731     
24732     // defaults : white / black...
24733     this.applyBlacklists();
24734     
24735     
24736     
24737 };
24738
24739
24740 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24741
24742
24743      /**
24744      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24745      */
24746     
24747     owner : false,
24748     
24749      /**
24750      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24751      *                        Roo.resizable.
24752      */
24753     resizable : false,
24754      /**
24755      * @cfg {Number} height (in pixels)
24756      */   
24757     height: 300,
24758    /**
24759      * @cfg {Number} width (in pixels)
24760      */   
24761     width: 500,
24762     
24763     /**
24764      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24765      * 
24766      */
24767     stylesheets: false,
24768     
24769     // id of frame..
24770     frameId: false,
24771     
24772     // private properties
24773     validationEvent : false,
24774     deferHeight: true,
24775     initialized : false,
24776     activated : false,
24777     sourceEditMode : false,
24778     onFocus : Roo.emptyFn,
24779     iframePad:3,
24780     hideMode:'offsets',
24781     
24782     clearUp: true,
24783     
24784     // blacklist + whitelisted elements..
24785     black: false,
24786     white: false,
24787      
24788     
24789
24790     /**
24791      * Protected method that will not generally be called directly. It
24792      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24793      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24794      */
24795     getDocMarkup : function(){
24796         // body styles..
24797         var st = '';
24798         
24799         // inherit styels from page...?? 
24800         if (this.stylesheets === false) {
24801             
24802             Roo.get(document.head).select('style').each(function(node) {
24803                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24804             });
24805             
24806             Roo.get(document.head).select('link').each(function(node) { 
24807                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24808             });
24809             
24810         } else if (!this.stylesheets.length) {
24811                 // simple..
24812                 st = '<style type="text/css">' +
24813                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24814                    '</style>';
24815         } else { 
24816             
24817         }
24818         
24819         st +=  '<style type="text/css">' +
24820             'IMG { cursor: pointer } ' +
24821         '</style>';
24822
24823         
24824         return '<html><head>' + st  +
24825             //<style type="text/css">' +
24826             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24827             //'</style>' +
24828             ' </head><body class="roo-htmleditor-body"></body></html>';
24829     },
24830
24831     // private
24832     onRender : function(ct, position)
24833     {
24834         var _t = this;
24835         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24836         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24837         
24838         
24839         this.el.dom.style.border = '0 none';
24840         this.el.dom.setAttribute('tabIndex', -1);
24841         this.el.addClass('x-hidden hide');
24842         
24843         
24844         
24845         if(Roo.isIE){ // fix IE 1px bogus margin
24846             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24847         }
24848        
24849         
24850         this.frameId = Roo.id();
24851         
24852          
24853         
24854         var iframe = this.owner.wrap.createChild({
24855             tag: 'iframe',
24856             cls: 'form-control', // bootstrap..
24857             id: this.frameId,
24858             name: this.frameId,
24859             frameBorder : 'no',
24860             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24861         }, this.el
24862         );
24863         
24864         
24865         this.iframe = iframe.dom;
24866
24867          this.assignDocWin();
24868         
24869         this.doc.designMode = 'on';
24870        
24871         this.doc.open();
24872         this.doc.write(this.getDocMarkup());
24873         this.doc.close();
24874
24875         
24876         var task = { // must defer to wait for browser to be ready
24877             run : function(){
24878                 //console.log("run task?" + this.doc.readyState);
24879                 this.assignDocWin();
24880                 if(this.doc.body || this.doc.readyState == 'complete'){
24881                     try {
24882                         this.doc.designMode="on";
24883                     } catch (e) {
24884                         return;
24885                     }
24886                     Roo.TaskMgr.stop(task);
24887                     this.initEditor.defer(10, this);
24888                 }
24889             },
24890             interval : 10,
24891             duration: 10000,
24892             scope: this
24893         };
24894         Roo.TaskMgr.start(task);
24895
24896     },
24897
24898     // private
24899     onResize : function(w, h)
24900     {
24901          Roo.log('resize: ' +w + ',' + h );
24902         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24903         if(!this.iframe){
24904             return;
24905         }
24906         if(typeof w == 'number'){
24907             
24908             this.iframe.style.width = w + 'px';
24909         }
24910         if(typeof h == 'number'){
24911             
24912             this.iframe.style.height = h + 'px';
24913             if(this.doc){
24914                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24915             }
24916         }
24917         
24918     },
24919
24920     /**
24921      * Toggles the editor between standard and source edit mode.
24922      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24923      */
24924     toggleSourceEdit : function(sourceEditMode){
24925         
24926         this.sourceEditMode = sourceEditMode === true;
24927         
24928         if(this.sourceEditMode){
24929  
24930             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24931             
24932         }else{
24933             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24934             //this.iframe.className = '';
24935             this.deferFocus();
24936         }
24937         //this.setSize(this.owner.wrap.getSize());
24938         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24939     },
24940
24941     
24942   
24943
24944     /**
24945      * Protected method that will not generally be called directly. If you need/want
24946      * custom HTML cleanup, this is the method you should override.
24947      * @param {String} html The HTML to be cleaned
24948      * return {String} The cleaned HTML
24949      */
24950     cleanHtml : function(html){
24951         html = String(html);
24952         if(html.length > 5){
24953             if(Roo.isSafari){ // strip safari nonsense
24954                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24955             }
24956         }
24957         if(html == '&nbsp;'){
24958             html = '';
24959         }
24960         return html;
24961     },
24962
24963     /**
24964      * HTML Editor -> Textarea
24965      * Protected method that will not generally be called directly. Syncs the contents
24966      * of the editor iframe with the textarea.
24967      */
24968     syncValue : function(){
24969         if(this.initialized){
24970             var bd = (this.doc.body || this.doc.documentElement);
24971             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24972             var html = bd.innerHTML;
24973             if(Roo.isSafari){
24974                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24975                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24976                 if(m && m[1]){
24977                     html = '<div style="'+m[0]+'">' + html + '</div>';
24978                 }
24979             }
24980             html = this.cleanHtml(html);
24981             // fix up the special chars.. normaly like back quotes in word...
24982             // however we do not want to do this with chinese..
24983             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24984                 var cc = b.charCodeAt();
24985                 if (
24986                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24987                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24988                     (cc >= 0xf900 && cc < 0xfb00 )
24989                 ) {
24990                         return b;
24991                 }
24992                 return "&#"+cc+";" 
24993             });
24994             if(this.owner.fireEvent('beforesync', this, html) !== false){
24995                 this.el.dom.value = html;
24996                 this.owner.fireEvent('sync', this, html);
24997             }
24998         }
24999     },
25000
25001     /**
25002      * Protected method that will not generally be called directly. Pushes the value of the textarea
25003      * into the iframe editor.
25004      */
25005     pushValue : function(){
25006         if(this.initialized){
25007             var v = this.el.dom.value.trim();
25008             
25009 //            if(v.length < 1){
25010 //                v = '&#160;';
25011 //            }
25012             
25013             if(this.owner.fireEvent('beforepush', this, v) !== false){
25014                 var d = (this.doc.body || this.doc.documentElement);
25015                 d.innerHTML = v;
25016                 this.cleanUpPaste();
25017                 this.el.dom.value = d.innerHTML;
25018                 this.owner.fireEvent('push', this, v);
25019             }
25020         }
25021     },
25022
25023     // private
25024     deferFocus : function(){
25025         this.focus.defer(10, this);
25026     },
25027
25028     // doc'ed in Field
25029     focus : function(){
25030         if(this.win && !this.sourceEditMode){
25031             this.win.focus();
25032         }else{
25033             this.el.focus();
25034         }
25035     },
25036     
25037     assignDocWin: function()
25038     {
25039         var iframe = this.iframe;
25040         
25041          if(Roo.isIE){
25042             this.doc = iframe.contentWindow.document;
25043             this.win = iframe.contentWindow;
25044         } else {
25045 //            if (!Roo.get(this.frameId)) {
25046 //                return;
25047 //            }
25048 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25049 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25050             
25051             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25052                 return;
25053             }
25054             
25055             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25056             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25057         }
25058     },
25059     
25060     // private
25061     initEditor : function(){
25062         //console.log("INIT EDITOR");
25063         this.assignDocWin();
25064         
25065         
25066         
25067         this.doc.designMode="on";
25068         this.doc.open();
25069         this.doc.write(this.getDocMarkup());
25070         this.doc.close();
25071         
25072         var dbody = (this.doc.body || this.doc.documentElement);
25073         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25074         // this copies styles from the containing element into thsi one..
25075         // not sure why we need all of this..
25076         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25077         
25078         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25079         //ss['background-attachment'] = 'fixed'; // w3c
25080         dbody.bgProperties = 'fixed'; // ie
25081         //Roo.DomHelper.applyStyles(dbody, ss);
25082         Roo.EventManager.on(this.doc, {
25083             //'mousedown': this.onEditorEvent,
25084             'mouseup': this.onEditorEvent,
25085             'dblclick': this.onEditorEvent,
25086             'click': this.onEditorEvent,
25087             'keyup': this.onEditorEvent,
25088             buffer:100,
25089             scope: this
25090         });
25091         if(Roo.isGecko){
25092             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25093         }
25094         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25095             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25096         }
25097         this.initialized = true;
25098
25099         this.owner.fireEvent('initialize', this);
25100         this.pushValue();
25101     },
25102
25103     // private
25104     onDestroy : function(){
25105         
25106         
25107         
25108         if(this.rendered){
25109             
25110             //for (var i =0; i < this.toolbars.length;i++) {
25111             //    // fixme - ask toolbars for heights?
25112             //    this.toolbars[i].onDestroy();
25113            // }
25114             
25115             //this.wrap.dom.innerHTML = '';
25116             //this.wrap.remove();
25117         }
25118     },
25119
25120     // private
25121     onFirstFocus : function(){
25122         
25123         this.assignDocWin();
25124         
25125         
25126         this.activated = true;
25127          
25128     
25129         if(Roo.isGecko){ // prevent silly gecko errors
25130             this.win.focus();
25131             var s = this.win.getSelection();
25132             if(!s.focusNode || s.focusNode.nodeType != 3){
25133                 var r = s.getRangeAt(0);
25134                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25135                 r.collapse(true);
25136                 this.deferFocus();
25137             }
25138             try{
25139                 this.execCmd('useCSS', true);
25140                 this.execCmd('styleWithCSS', false);
25141             }catch(e){}
25142         }
25143         this.owner.fireEvent('activate', this);
25144     },
25145
25146     // private
25147     adjustFont: function(btn){
25148         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25149         //if(Roo.isSafari){ // safari
25150         //    adjust *= 2;
25151        // }
25152         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25153         if(Roo.isSafari){ // safari
25154             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25155             v =  (v < 10) ? 10 : v;
25156             v =  (v > 48) ? 48 : v;
25157             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25158             
25159         }
25160         
25161         
25162         v = Math.max(1, v+adjust);
25163         
25164         this.execCmd('FontSize', v  );
25165     },
25166
25167     onEditorEvent : function(e)
25168     {
25169         this.owner.fireEvent('editorevent', this, e);
25170       //  this.updateToolbar();
25171         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25172     },
25173
25174     insertTag : function(tg)
25175     {
25176         // could be a bit smarter... -> wrap the current selected tRoo..
25177         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25178             
25179             range = this.createRange(this.getSelection());
25180             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25181             wrappingNode.appendChild(range.extractContents());
25182             range.insertNode(wrappingNode);
25183
25184             return;
25185             
25186             
25187             
25188         }
25189         this.execCmd("formatblock",   tg);
25190         
25191     },
25192     
25193     insertText : function(txt)
25194     {
25195         
25196         
25197         var range = this.createRange();
25198         range.deleteContents();
25199                //alert(Sender.getAttribute('label'));
25200                
25201         range.insertNode(this.doc.createTextNode(txt));
25202     } ,
25203     
25204      
25205
25206     /**
25207      * Executes a Midas editor command on the editor document and performs necessary focus and
25208      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25209      * @param {String} cmd The Midas command
25210      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25211      */
25212     relayCmd : function(cmd, value){
25213         this.win.focus();
25214         this.execCmd(cmd, value);
25215         this.owner.fireEvent('editorevent', this);
25216         //this.updateToolbar();
25217         this.owner.deferFocus();
25218     },
25219
25220     /**
25221      * Executes a Midas editor command directly on the editor document.
25222      * For visual commands, you should use {@link #relayCmd} instead.
25223      * <b>This should only be called after the editor is initialized.</b>
25224      * @param {String} cmd The Midas command
25225      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25226      */
25227     execCmd : function(cmd, value){
25228         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25229         this.syncValue();
25230     },
25231  
25232  
25233    
25234     /**
25235      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25236      * to insert tRoo.
25237      * @param {String} text | dom node.. 
25238      */
25239     insertAtCursor : function(text)
25240     {
25241         
25242         
25243         
25244         if(!this.activated){
25245             return;
25246         }
25247         /*
25248         if(Roo.isIE){
25249             this.win.focus();
25250             var r = this.doc.selection.createRange();
25251             if(r){
25252                 r.collapse(true);
25253                 r.pasteHTML(text);
25254                 this.syncValue();
25255                 this.deferFocus();
25256             
25257             }
25258             return;
25259         }
25260         */
25261         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25262             this.win.focus();
25263             
25264             
25265             // from jquery ui (MIT licenced)
25266             var range, node;
25267             var win = this.win;
25268             
25269             if (win.getSelection && win.getSelection().getRangeAt) {
25270                 range = win.getSelection().getRangeAt(0);
25271                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25272                 range.insertNode(node);
25273             } else if (win.document.selection && win.document.selection.createRange) {
25274                 // no firefox support
25275                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25276                 win.document.selection.createRange().pasteHTML(txt);
25277             } else {
25278                 // no firefox support
25279                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25280                 this.execCmd('InsertHTML', txt);
25281             } 
25282             
25283             this.syncValue();
25284             
25285             this.deferFocus();
25286         }
25287     },
25288  // private
25289     mozKeyPress : function(e){
25290         if(e.ctrlKey){
25291             var c = e.getCharCode(), cmd;
25292           
25293             if(c > 0){
25294                 c = String.fromCharCode(c).toLowerCase();
25295                 switch(c){
25296                     case 'b':
25297                         cmd = 'bold';
25298                         break;
25299                     case 'i':
25300                         cmd = 'italic';
25301                         break;
25302                     
25303                     case 'u':
25304                         cmd = 'underline';
25305                         break;
25306                     
25307                     case 'v':
25308                         this.cleanUpPaste.defer(100, this);
25309                         return;
25310                         
25311                 }
25312                 if(cmd){
25313                     this.win.focus();
25314                     this.execCmd(cmd);
25315                     this.deferFocus();
25316                     e.preventDefault();
25317                 }
25318                 
25319             }
25320         }
25321     },
25322
25323     // private
25324     fixKeys : function(){ // load time branching for fastest keydown performance
25325         if(Roo.isIE){
25326             return function(e){
25327                 var k = e.getKey(), r;
25328                 if(k == e.TAB){
25329                     e.stopEvent();
25330                     r = this.doc.selection.createRange();
25331                     if(r){
25332                         r.collapse(true);
25333                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25334                         this.deferFocus();
25335                     }
25336                     return;
25337                 }
25338                 
25339                 if(k == e.ENTER){
25340                     r = this.doc.selection.createRange();
25341                     if(r){
25342                         var target = r.parentElement();
25343                         if(!target || target.tagName.toLowerCase() != 'li'){
25344                             e.stopEvent();
25345                             r.pasteHTML('<br />');
25346                             r.collapse(false);
25347                             r.select();
25348                         }
25349                     }
25350                 }
25351                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25352                     this.cleanUpPaste.defer(100, this);
25353                     return;
25354                 }
25355                 
25356                 
25357             };
25358         }else if(Roo.isOpera){
25359             return function(e){
25360                 var k = e.getKey();
25361                 if(k == e.TAB){
25362                     e.stopEvent();
25363                     this.win.focus();
25364                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25365                     this.deferFocus();
25366                 }
25367                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25368                     this.cleanUpPaste.defer(100, this);
25369                     return;
25370                 }
25371                 
25372             };
25373         }else if(Roo.isSafari){
25374             return function(e){
25375                 var k = e.getKey();
25376                 
25377                 if(k == e.TAB){
25378                     e.stopEvent();
25379                     this.execCmd('InsertText','\t');
25380                     this.deferFocus();
25381                     return;
25382                 }
25383                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25384                     this.cleanUpPaste.defer(100, this);
25385                     return;
25386                 }
25387                 
25388              };
25389         }
25390     }(),
25391     
25392     getAllAncestors: function()
25393     {
25394         var p = this.getSelectedNode();
25395         var a = [];
25396         if (!p) {
25397             a.push(p); // push blank onto stack..
25398             p = this.getParentElement();
25399         }
25400         
25401         
25402         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25403             a.push(p);
25404             p = p.parentNode;
25405         }
25406         a.push(this.doc.body);
25407         return a;
25408     },
25409     lastSel : false,
25410     lastSelNode : false,
25411     
25412     
25413     getSelection : function() 
25414     {
25415         this.assignDocWin();
25416         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25417     },
25418     
25419     getSelectedNode: function() 
25420     {
25421         // this may only work on Gecko!!!
25422         
25423         // should we cache this!!!!
25424         
25425         
25426         
25427          
25428         var range = this.createRange(this.getSelection()).cloneRange();
25429         
25430         if (Roo.isIE) {
25431             var parent = range.parentElement();
25432             while (true) {
25433                 var testRange = range.duplicate();
25434                 testRange.moveToElementText(parent);
25435                 if (testRange.inRange(range)) {
25436                     break;
25437                 }
25438                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25439                     break;
25440                 }
25441                 parent = parent.parentElement;
25442             }
25443             return parent;
25444         }
25445         
25446         // is ancestor a text element.
25447         var ac =  range.commonAncestorContainer;
25448         if (ac.nodeType == 3) {
25449             ac = ac.parentNode;
25450         }
25451         
25452         var ar = ac.childNodes;
25453          
25454         var nodes = [];
25455         var other_nodes = [];
25456         var has_other_nodes = false;
25457         for (var i=0;i<ar.length;i++) {
25458             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25459                 continue;
25460             }
25461             // fullly contained node.
25462             
25463             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25464                 nodes.push(ar[i]);
25465                 continue;
25466             }
25467             
25468             // probably selected..
25469             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25470                 other_nodes.push(ar[i]);
25471                 continue;
25472             }
25473             // outer..
25474             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25475                 continue;
25476             }
25477             
25478             
25479             has_other_nodes = true;
25480         }
25481         if (!nodes.length && other_nodes.length) {
25482             nodes= other_nodes;
25483         }
25484         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25485             return false;
25486         }
25487         
25488         return nodes[0];
25489     },
25490     createRange: function(sel)
25491     {
25492         // this has strange effects when using with 
25493         // top toolbar - not sure if it's a great idea.
25494         //this.editor.contentWindow.focus();
25495         if (typeof sel != "undefined") {
25496             try {
25497                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25498             } catch(e) {
25499                 return this.doc.createRange();
25500             }
25501         } else {
25502             return this.doc.createRange();
25503         }
25504     },
25505     getParentElement: function()
25506     {
25507         
25508         this.assignDocWin();
25509         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25510         
25511         var range = this.createRange(sel);
25512          
25513         try {
25514             var p = range.commonAncestorContainer;
25515             while (p.nodeType == 3) { // text node
25516                 p = p.parentNode;
25517             }
25518             return p;
25519         } catch (e) {
25520             return null;
25521         }
25522     
25523     },
25524     /***
25525      *
25526      * Range intersection.. the hard stuff...
25527      *  '-1' = before
25528      *  '0' = hits..
25529      *  '1' = after.
25530      *         [ -- selected range --- ]
25531      *   [fail]                        [fail]
25532      *
25533      *    basically..
25534      *      if end is before start or  hits it. fail.
25535      *      if start is after end or hits it fail.
25536      *
25537      *   if either hits (but other is outside. - then it's not 
25538      *   
25539      *    
25540      **/
25541     
25542     
25543     // @see http://www.thismuchiknow.co.uk/?p=64.
25544     rangeIntersectsNode : function(range, node)
25545     {
25546         var nodeRange = node.ownerDocument.createRange();
25547         try {
25548             nodeRange.selectNode(node);
25549         } catch (e) {
25550             nodeRange.selectNodeContents(node);
25551         }
25552     
25553         var rangeStartRange = range.cloneRange();
25554         rangeStartRange.collapse(true);
25555     
25556         var rangeEndRange = range.cloneRange();
25557         rangeEndRange.collapse(false);
25558     
25559         var nodeStartRange = nodeRange.cloneRange();
25560         nodeStartRange.collapse(true);
25561     
25562         var nodeEndRange = nodeRange.cloneRange();
25563         nodeEndRange.collapse(false);
25564     
25565         return rangeStartRange.compareBoundaryPoints(
25566                  Range.START_TO_START, nodeEndRange) == -1 &&
25567                rangeEndRange.compareBoundaryPoints(
25568                  Range.START_TO_START, nodeStartRange) == 1;
25569         
25570          
25571     },
25572     rangeCompareNode : function(range, node)
25573     {
25574         var nodeRange = node.ownerDocument.createRange();
25575         try {
25576             nodeRange.selectNode(node);
25577         } catch (e) {
25578             nodeRange.selectNodeContents(node);
25579         }
25580         
25581         
25582         range.collapse(true);
25583     
25584         nodeRange.collapse(true);
25585      
25586         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25587         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25588          
25589         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25590         
25591         var nodeIsBefore   =  ss == 1;
25592         var nodeIsAfter    = ee == -1;
25593         
25594         if (nodeIsBefore && nodeIsAfter)
25595             return 0; // outer
25596         if (!nodeIsBefore && nodeIsAfter)
25597             return 1; //right trailed.
25598         
25599         if (nodeIsBefore && !nodeIsAfter)
25600             return 2;  // left trailed.
25601         // fully contined.
25602         return 3;
25603     },
25604
25605     // private? - in a new class?
25606     cleanUpPaste :  function()
25607     {
25608         // cleans up the whole document..
25609         Roo.log('cleanuppaste');
25610         
25611         this.cleanUpChildren(this.doc.body);
25612         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25613         if (clean != this.doc.body.innerHTML) {
25614             this.doc.body.innerHTML = clean;
25615         }
25616         
25617     },
25618     
25619     cleanWordChars : function(input) {// change the chars to hex code
25620         var he = Roo.HtmlEditorCore;
25621         
25622         var output = input;
25623         Roo.each(he.swapCodes, function(sw) { 
25624             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25625             
25626             output = output.replace(swapper, sw[1]);
25627         });
25628         
25629         return output;
25630     },
25631     
25632     
25633     cleanUpChildren : function (n)
25634     {
25635         if (!n.childNodes.length) {
25636             return;
25637         }
25638         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25639            this.cleanUpChild(n.childNodes[i]);
25640         }
25641     },
25642     
25643     
25644         
25645     
25646     cleanUpChild : function (node)
25647     {
25648         var ed = this;
25649         //console.log(node);
25650         if (node.nodeName == "#text") {
25651             // clean up silly Windows -- stuff?
25652             return; 
25653         }
25654         if (node.nodeName == "#comment") {
25655             node.parentNode.removeChild(node);
25656             // clean up silly Windows -- stuff?
25657             return; 
25658         }
25659         var lcname = node.tagName.toLowerCase();
25660         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25661         // whitelist of tags..
25662         
25663         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25664             // remove node.
25665             node.parentNode.removeChild(node);
25666             return;
25667             
25668         }
25669         
25670         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25671         
25672         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25673         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25674         
25675         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25676         //    remove_keep_children = true;
25677         //}
25678         
25679         if (remove_keep_children) {
25680             this.cleanUpChildren(node);
25681             // inserts everything just before this node...
25682             while (node.childNodes.length) {
25683                 var cn = node.childNodes[0];
25684                 node.removeChild(cn);
25685                 node.parentNode.insertBefore(cn, node);
25686             }
25687             node.parentNode.removeChild(node);
25688             return;
25689         }
25690         
25691         if (!node.attributes || !node.attributes.length) {
25692             this.cleanUpChildren(node);
25693             return;
25694         }
25695         
25696         function cleanAttr(n,v)
25697         {
25698             
25699             if (v.match(/^\./) || v.match(/^\//)) {
25700                 return;
25701             }
25702             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25703                 return;
25704             }
25705             if (v.match(/^#/)) {
25706                 return;
25707             }
25708 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25709             node.removeAttribute(n);
25710             
25711         }
25712         
25713         var cwhite = this.cwhite;
25714         var cblack = this.cblack;
25715             
25716         function cleanStyle(n,v)
25717         {
25718             if (v.match(/expression/)) { //XSS?? should we even bother..
25719                 node.removeAttribute(n);
25720                 return;
25721             }
25722             
25723             var parts = v.split(/;/);
25724             var clean = [];
25725             
25726             Roo.each(parts, function(p) {
25727                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25728                 if (!p.length) {
25729                     return true;
25730                 }
25731                 var l = p.split(':').shift().replace(/\s+/g,'');
25732                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25733                 
25734                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25735 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25736                     //node.removeAttribute(n);
25737                     return true;
25738                 }
25739                 //Roo.log()
25740                 // only allow 'c whitelisted system attributes'
25741                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25742 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25743                     //node.removeAttribute(n);
25744                     return true;
25745                 }
25746                 
25747                 
25748                  
25749                 
25750                 clean.push(p);
25751                 return true;
25752             });
25753             if (clean.length) { 
25754                 node.setAttribute(n, clean.join(';'));
25755             } else {
25756                 node.removeAttribute(n);
25757             }
25758             
25759         }
25760         
25761         
25762         for (var i = node.attributes.length-1; i > -1 ; i--) {
25763             var a = node.attributes[i];
25764             //console.log(a);
25765             
25766             if (a.name.toLowerCase().substr(0,2)=='on')  {
25767                 node.removeAttribute(a.name);
25768                 continue;
25769             }
25770             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25771                 node.removeAttribute(a.name);
25772                 continue;
25773             }
25774             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25775                 cleanAttr(a.name,a.value); // fixme..
25776                 continue;
25777             }
25778             if (a.name == 'style') {
25779                 cleanStyle(a.name,a.value);
25780                 continue;
25781             }
25782             /// clean up MS crap..
25783             // tecnically this should be a list of valid class'es..
25784             
25785             
25786             if (a.name == 'class') {
25787                 if (a.value.match(/^Mso/)) {
25788                     node.className = '';
25789                 }
25790                 
25791                 if (a.value.match(/body/)) {
25792                     node.className = '';
25793                 }
25794                 continue;
25795             }
25796             
25797             // style cleanup!?
25798             // class cleanup?
25799             
25800         }
25801         
25802         
25803         this.cleanUpChildren(node);
25804         
25805         
25806     },
25807     
25808     /**
25809      * Clean up MS wordisms...
25810      */
25811     cleanWord : function(node)
25812     {
25813         
25814         
25815         if (!node) {
25816             this.cleanWord(this.doc.body);
25817             return;
25818         }
25819         if (node.nodeName == "#text") {
25820             // clean up silly Windows -- stuff?
25821             return; 
25822         }
25823         if (node.nodeName == "#comment") {
25824             node.parentNode.removeChild(node);
25825             // clean up silly Windows -- stuff?
25826             return; 
25827         }
25828         
25829         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25830             node.parentNode.removeChild(node);
25831             return;
25832         }
25833         
25834         // remove - but keep children..
25835         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25836             while (node.childNodes.length) {
25837                 var cn = node.childNodes[0];
25838                 node.removeChild(cn);
25839                 node.parentNode.insertBefore(cn, node);
25840             }
25841             node.parentNode.removeChild(node);
25842             this.iterateChildren(node, this.cleanWord);
25843             return;
25844         }
25845         // clean styles
25846         if (node.className.length) {
25847             
25848             var cn = node.className.split(/\W+/);
25849             var cna = [];
25850             Roo.each(cn, function(cls) {
25851                 if (cls.match(/Mso[a-zA-Z]+/)) {
25852                     return;
25853                 }
25854                 cna.push(cls);
25855             });
25856             node.className = cna.length ? cna.join(' ') : '';
25857             if (!cna.length) {
25858                 node.removeAttribute("class");
25859             }
25860         }
25861         
25862         if (node.hasAttribute("lang")) {
25863             node.removeAttribute("lang");
25864         }
25865         
25866         if (node.hasAttribute("style")) {
25867             
25868             var styles = node.getAttribute("style").split(";");
25869             var nstyle = [];
25870             Roo.each(styles, function(s) {
25871                 if (!s.match(/:/)) {
25872                     return;
25873                 }
25874                 var kv = s.split(":");
25875                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25876                     return;
25877                 }
25878                 // what ever is left... we allow.
25879                 nstyle.push(s);
25880             });
25881             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25882             if (!nstyle.length) {
25883                 node.removeAttribute('style');
25884             }
25885         }
25886         this.iterateChildren(node, this.cleanWord);
25887         
25888         
25889         
25890     },
25891     /**
25892      * iterateChildren of a Node, calling fn each time, using this as the scole..
25893      * @param {DomNode} node node to iterate children of.
25894      * @param {Function} fn method of this class to call on each item.
25895      */
25896     iterateChildren : function(node, fn)
25897     {
25898         if (!node.childNodes.length) {
25899                 return;
25900         }
25901         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25902            fn.call(this, node.childNodes[i])
25903         }
25904     },
25905     
25906     
25907     /**
25908      * cleanTableWidths.
25909      *
25910      * Quite often pasting from word etc.. results in tables with column and widths.
25911      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25912      *
25913      */
25914     cleanTableWidths : function(node)
25915     {
25916          
25917          
25918         if (!node) {
25919             this.cleanTableWidths(this.doc.body);
25920             return;
25921         }
25922         
25923         // ignore list...
25924         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25925             return; 
25926         }
25927         Roo.log(node.tagName);
25928         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25929             this.iterateChildren(node, this.cleanTableWidths);
25930             return;
25931         }
25932         if (node.hasAttribute('width')) {
25933             node.removeAttribute('width');
25934         }
25935         
25936          
25937         if (node.hasAttribute("style")) {
25938             // pretty basic...
25939             
25940             var styles = node.getAttribute("style").split(";");
25941             var nstyle = [];
25942             Roo.each(styles, function(s) {
25943                 if (!s.match(/:/)) {
25944                     return;
25945                 }
25946                 var kv = s.split(":");
25947                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25948                     return;
25949                 }
25950                 // what ever is left... we allow.
25951                 nstyle.push(s);
25952             });
25953             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25954             if (!nstyle.length) {
25955                 node.removeAttribute('style');
25956             }
25957         }
25958         
25959         this.iterateChildren(node, this.cleanTableWidths);
25960         
25961         
25962     },
25963     
25964     
25965     
25966     
25967     domToHTML : function(currentElement, depth, nopadtext) {
25968         
25969         depth = depth || 0;
25970         nopadtext = nopadtext || false;
25971     
25972         if (!currentElement) {
25973             return this.domToHTML(this.doc.body);
25974         }
25975         
25976         //Roo.log(currentElement);
25977         var j;
25978         var allText = false;
25979         var nodeName = currentElement.nodeName;
25980         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25981         
25982         if  (nodeName == '#text') {
25983             
25984             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25985         }
25986         
25987         
25988         var ret = '';
25989         if (nodeName != 'BODY') {
25990              
25991             var i = 0;
25992             // Prints the node tagName, such as <A>, <IMG>, etc
25993             if (tagName) {
25994                 var attr = [];
25995                 for(i = 0; i < currentElement.attributes.length;i++) {
25996                     // quoting?
25997                     var aname = currentElement.attributes.item(i).name;
25998                     if (!currentElement.attributes.item(i).value.length) {
25999                         continue;
26000                     }
26001                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26002                 }
26003                 
26004                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26005             } 
26006             else {
26007                 
26008                 // eack
26009             }
26010         } else {
26011             tagName = false;
26012         }
26013         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26014             return ret;
26015         }
26016         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26017             nopadtext = true;
26018         }
26019         
26020         
26021         // Traverse the tree
26022         i = 0;
26023         var currentElementChild = currentElement.childNodes.item(i);
26024         var allText = true;
26025         var innerHTML  = '';
26026         lastnode = '';
26027         while (currentElementChild) {
26028             // Formatting code (indent the tree so it looks nice on the screen)
26029             var nopad = nopadtext;
26030             if (lastnode == 'SPAN') {
26031                 nopad  = true;
26032             }
26033             // text
26034             if  (currentElementChild.nodeName == '#text') {
26035                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26036                 toadd = nopadtext ? toadd : toadd.trim();
26037                 if (!nopad && toadd.length > 80) {
26038                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26039                 }
26040                 innerHTML  += toadd;
26041                 
26042                 i++;
26043                 currentElementChild = currentElement.childNodes.item(i);
26044                 lastNode = '';
26045                 continue;
26046             }
26047             allText = false;
26048             
26049             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26050                 
26051             // Recursively traverse the tree structure of the child node
26052             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26053             lastnode = currentElementChild.nodeName;
26054             i++;
26055             currentElementChild=currentElement.childNodes.item(i);
26056         }
26057         
26058         ret += innerHTML;
26059         
26060         if (!allText) {
26061                 // The remaining code is mostly for formatting the tree
26062             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26063         }
26064         
26065         
26066         if (tagName) {
26067             ret+= "</"+tagName+">";
26068         }
26069         return ret;
26070         
26071     },
26072         
26073     applyBlacklists : function()
26074     {
26075         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26076         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26077         
26078         this.white = [];
26079         this.black = [];
26080         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26081             if (b.indexOf(tag) > -1) {
26082                 return;
26083             }
26084             this.white.push(tag);
26085             
26086         }, this);
26087         
26088         Roo.each(w, function(tag) {
26089             if (b.indexOf(tag) > -1) {
26090                 return;
26091             }
26092             if (this.white.indexOf(tag) > -1) {
26093                 return;
26094             }
26095             this.white.push(tag);
26096             
26097         }, this);
26098         
26099         
26100         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26101             if (w.indexOf(tag) > -1) {
26102                 return;
26103             }
26104             this.black.push(tag);
26105             
26106         }, this);
26107         
26108         Roo.each(b, function(tag) {
26109             if (w.indexOf(tag) > -1) {
26110                 return;
26111             }
26112             if (this.black.indexOf(tag) > -1) {
26113                 return;
26114             }
26115             this.black.push(tag);
26116             
26117         }, this);
26118         
26119         
26120         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26121         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26122         
26123         this.cwhite = [];
26124         this.cblack = [];
26125         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26126             if (b.indexOf(tag) > -1) {
26127                 return;
26128             }
26129             this.cwhite.push(tag);
26130             
26131         }, this);
26132         
26133         Roo.each(w, function(tag) {
26134             if (b.indexOf(tag) > -1) {
26135                 return;
26136             }
26137             if (this.cwhite.indexOf(tag) > -1) {
26138                 return;
26139             }
26140             this.cwhite.push(tag);
26141             
26142         }, this);
26143         
26144         
26145         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26146             if (w.indexOf(tag) > -1) {
26147                 return;
26148             }
26149             this.cblack.push(tag);
26150             
26151         }, this);
26152         
26153         Roo.each(b, function(tag) {
26154             if (w.indexOf(tag) > -1) {
26155                 return;
26156             }
26157             if (this.cblack.indexOf(tag) > -1) {
26158                 return;
26159             }
26160             this.cblack.push(tag);
26161             
26162         }, this);
26163     },
26164     
26165     setStylesheets : function(stylesheets)
26166     {
26167         if(typeof(stylesheets) == 'string'){
26168             Roo.get(this.iframe.contentDocument.head).createChild({
26169                 tag : 'link',
26170                 rel : 'stylesheet',
26171                 type : 'text/css',
26172                 href : stylesheets
26173             });
26174             
26175             return;
26176         }
26177         var _this = this;
26178      
26179         Roo.each(stylesheets, function(s) {
26180             if(!s.length){
26181                 return;
26182             }
26183             
26184             Roo.get(_this.iframe.contentDocument.head).createChild({
26185                 tag : 'link',
26186                 rel : 'stylesheet',
26187                 type : 'text/css',
26188                 href : s
26189             });
26190         });
26191
26192         
26193     },
26194     
26195     removeStylesheets : function()
26196     {
26197         var _this = this;
26198         
26199         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26200             s.remove();
26201         });
26202     }
26203     
26204     // hide stuff that is not compatible
26205     /**
26206      * @event blur
26207      * @hide
26208      */
26209     /**
26210      * @event change
26211      * @hide
26212      */
26213     /**
26214      * @event focus
26215      * @hide
26216      */
26217     /**
26218      * @event specialkey
26219      * @hide
26220      */
26221     /**
26222      * @cfg {String} fieldClass @hide
26223      */
26224     /**
26225      * @cfg {String} focusClass @hide
26226      */
26227     /**
26228      * @cfg {String} autoCreate @hide
26229      */
26230     /**
26231      * @cfg {String} inputType @hide
26232      */
26233     /**
26234      * @cfg {String} invalidClass @hide
26235      */
26236     /**
26237      * @cfg {String} invalidText @hide
26238      */
26239     /**
26240      * @cfg {String} msgFx @hide
26241      */
26242     /**
26243      * @cfg {String} validateOnBlur @hide
26244      */
26245 });
26246
26247 Roo.HtmlEditorCore.white = [
26248         'area', 'br', 'img', 'input', 'hr', 'wbr',
26249         
26250        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26251        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26252        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26253        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26254        'table',   'ul',         'xmp', 
26255        
26256        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26257       'thead',   'tr', 
26258      
26259       'dir', 'menu', 'ol', 'ul', 'dl',
26260        
26261       'embed',  'object'
26262 ];
26263
26264
26265 Roo.HtmlEditorCore.black = [
26266     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26267         'applet', // 
26268         'base',   'basefont', 'bgsound', 'blink',  'body', 
26269         'frame',  'frameset', 'head',    'html',   'ilayer', 
26270         'iframe', 'layer',  'link',     'meta',    'object',   
26271         'script', 'style' ,'title',  'xml' // clean later..
26272 ];
26273 Roo.HtmlEditorCore.clean = [
26274     'script', 'style', 'title', 'xml'
26275 ];
26276 Roo.HtmlEditorCore.remove = [
26277     'font'
26278 ];
26279 // attributes..
26280
26281 Roo.HtmlEditorCore.ablack = [
26282     'on'
26283 ];
26284     
26285 Roo.HtmlEditorCore.aclean = [ 
26286     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26287 ];
26288
26289 // protocols..
26290 Roo.HtmlEditorCore.pwhite= [
26291         'http',  'https',  'mailto'
26292 ];
26293
26294 // white listed style attributes.
26295 Roo.HtmlEditorCore.cwhite= [
26296       //  'text-align', /// default is to allow most things..
26297       
26298          
26299 //        'font-size'//??
26300 ];
26301
26302 // black listed style attributes.
26303 Roo.HtmlEditorCore.cblack= [
26304       //  'font-size' -- this can be set by the project 
26305 ];
26306
26307
26308 Roo.HtmlEditorCore.swapCodes   =[ 
26309     [    8211, "--" ], 
26310     [    8212, "--" ], 
26311     [    8216,  "'" ],  
26312     [    8217, "'" ],  
26313     [    8220, '"' ],  
26314     [    8221, '"' ],  
26315     [    8226, "*" ],  
26316     [    8230, "..." ]
26317 ]; 
26318
26319     //<script type="text/javascript">
26320
26321 /*
26322  * Ext JS Library 1.1.1
26323  * Copyright(c) 2006-2007, Ext JS, LLC.
26324  * Licence LGPL
26325  * 
26326  */
26327  
26328  
26329 Roo.form.HtmlEditor = function(config){
26330     
26331     
26332     
26333     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26334     
26335     if (!this.toolbars) {
26336         this.toolbars = [];
26337     }
26338     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26339     
26340     
26341 };
26342
26343 /**
26344  * @class Roo.form.HtmlEditor
26345  * @extends Roo.form.Field
26346  * Provides a lightweight HTML Editor component.
26347  *
26348  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26349  * 
26350  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26351  * supported by this editor.</b><br/><br/>
26352  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26353  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26354  */
26355 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26356     /**
26357      * @cfg {Boolean} clearUp
26358      */
26359     clearUp : true,
26360       /**
26361      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26362      */
26363     toolbars : false,
26364    
26365      /**
26366      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26367      *                        Roo.resizable.
26368      */
26369     resizable : false,
26370      /**
26371      * @cfg {Number} height (in pixels)
26372      */   
26373     height: 300,
26374    /**
26375      * @cfg {Number} width (in pixels)
26376      */   
26377     width: 500,
26378     
26379     /**
26380      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26381      * 
26382      */
26383     stylesheets: false,
26384     
26385     
26386      /**
26387      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26388      * 
26389      */
26390     cblack: false,
26391     /**
26392      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26393      * 
26394      */
26395     cwhite: false,
26396     
26397      /**
26398      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26399      * 
26400      */
26401     black: false,
26402     /**
26403      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26404      * 
26405      */
26406     white: false,
26407     
26408     // id of frame..
26409     frameId: false,
26410     
26411     // private properties
26412     validationEvent : false,
26413     deferHeight: true,
26414     initialized : false,
26415     activated : false,
26416     
26417     onFocus : Roo.emptyFn,
26418     iframePad:3,
26419     hideMode:'offsets',
26420     
26421     actionMode : 'container', // defaults to hiding it...
26422     
26423     defaultAutoCreate : { // modified by initCompnoent..
26424         tag: "textarea",
26425         style:"width:500px;height:300px;",
26426         autocomplete: "new-password"
26427     },
26428
26429     // private
26430     initComponent : function(){
26431         this.addEvents({
26432             /**
26433              * @event initialize
26434              * Fires when the editor is fully initialized (including the iframe)
26435              * @param {HtmlEditor} this
26436              */
26437             initialize: true,
26438             /**
26439              * @event activate
26440              * Fires when the editor is first receives the focus. Any insertion must wait
26441              * until after this event.
26442              * @param {HtmlEditor} this
26443              */
26444             activate: true,
26445              /**
26446              * @event beforesync
26447              * Fires before the textarea is updated with content from the editor iframe. Return false
26448              * to cancel the sync.
26449              * @param {HtmlEditor} this
26450              * @param {String} html
26451              */
26452             beforesync: true,
26453              /**
26454              * @event beforepush
26455              * Fires before the iframe editor is updated with content from the textarea. Return false
26456              * to cancel the push.
26457              * @param {HtmlEditor} this
26458              * @param {String} html
26459              */
26460             beforepush: true,
26461              /**
26462              * @event sync
26463              * Fires when the textarea is updated with content from the editor iframe.
26464              * @param {HtmlEditor} this
26465              * @param {String} html
26466              */
26467             sync: true,
26468              /**
26469              * @event push
26470              * Fires when the iframe editor is updated with content from the textarea.
26471              * @param {HtmlEditor} this
26472              * @param {String} html
26473              */
26474             push: true,
26475              /**
26476              * @event editmodechange
26477              * Fires when the editor switches edit modes
26478              * @param {HtmlEditor} this
26479              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26480              */
26481             editmodechange: true,
26482             /**
26483              * @event editorevent
26484              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26485              * @param {HtmlEditor} this
26486              */
26487             editorevent: true,
26488             /**
26489              * @event firstfocus
26490              * Fires when on first focus - needed by toolbars..
26491              * @param {HtmlEditor} this
26492              */
26493             firstfocus: true,
26494             /**
26495              * @event autosave
26496              * Auto save the htmlEditor value as a file into Events
26497              * @param {HtmlEditor} this
26498              */
26499             autosave: true,
26500             /**
26501              * @event savedpreview
26502              * preview the saved version of htmlEditor
26503              * @param {HtmlEditor} this
26504              */
26505             savedpreview: true,
26506             
26507             /**
26508             * @event stylesheetsclick
26509             * Fires when press the Sytlesheets button
26510             * @param {Roo.HtmlEditorCore} this
26511             */
26512             stylesheetsclick: true
26513         });
26514         this.defaultAutoCreate =  {
26515             tag: "textarea",
26516             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26517             autocomplete: "new-password"
26518         };
26519     },
26520
26521     /**
26522      * Protected method that will not generally be called directly. It
26523      * is called when the editor creates its toolbar. Override this method if you need to
26524      * add custom toolbar buttons.
26525      * @param {HtmlEditor} editor
26526      */
26527     createToolbar : function(editor){
26528         Roo.log("create toolbars");
26529         if (!editor.toolbars || !editor.toolbars.length) {
26530             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26531         }
26532         
26533         for (var i =0 ; i < editor.toolbars.length;i++) {
26534             editor.toolbars[i] = Roo.factory(
26535                     typeof(editor.toolbars[i]) == 'string' ?
26536                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26537                 Roo.form.HtmlEditor);
26538             editor.toolbars[i].init(editor);
26539         }
26540          
26541         
26542     },
26543
26544      
26545     // private
26546     onRender : function(ct, position)
26547     {
26548         var _t = this;
26549         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26550         
26551         this.wrap = this.el.wrap({
26552             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26553         });
26554         
26555         this.editorcore.onRender(ct, position);
26556          
26557         if (this.resizable) {
26558             this.resizeEl = new Roo.Resizable(this.wrap, {
26559                 pinned : true,
26560                 wrap: true,
26561                 dynamic : true,
26562                 minHeight : this.height,
26563                 height: this.height,
26564                 handles : this.resizable,
26565                 width: this.width,
26566                 listeners : {
26567                     resize : function(r, w, h) {
26568                         _t.onResize(w,h); // -something
26569                     }
26570                 }
26571             });
26572             
26573         }
26574         this.createToolbar(this);
26575        
26576         
26577         if(!this.width){
26578             this.setSize(this.wrap.getSize());
26579         }
26580         if (this.resizeEl) {
26581             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26582             // should trigger onReize..
26583         }
26584         
26585         this.keyNav = new Roo.KeyNav(this.el, {
26586             
26587             "tab" : function(e){
26588                 e.preventDefault();
26589                 
26590                 var value = this.getValue();
26591                 
26592                 var start = this.el.dom.selectionStart;
26593                 var end = this.el.dom.selectionEnd;
26594                 
26595                 if(!e.shiftKey){
26596                     
26597                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26598                     this.el.dom.setSelectionRange(end + 1, end + 1);
26599                     return;
26600                 }
26601                 
26602                 var f = value.substring(0, start).split("\t");
26603                 
26604                 if(f.pop().length != 0){
26605                     return;
26606                 }
26607                 
26608                 this.setValue(f.join("\t") + value.substring(end));
26609                 this.el.dom.setSelectionRange(start - 1, start - 1);
26610                 
26611             },
26612             
26613             "home" : function(e){
26614                 e.preventDefault();
26615                 
26616                 var curr = this.el.dom.selectionStart;
26617                 var lines = this.getValue().split("\n");
26618                 
26619                 if(!lines.length){
26620                     return;
26621                 }
26622                 
26623                 if(e.ctrlKey){
26624                     this.el.dom.setSelectionRange(0, 0);
26625                     return;
26626                 }
26627                 
26628                 var pos = 0;
26629                 
26630                 for (var i = 0; i < lines.length;i++) {
26631                     pos += lines[i].length;
26632                     
26633                     if(i != 0){
26634                         pos += 1;
26635                     }
26636                     
26637                     if(pos < curr){
26638                         continue;
26639                     }
26640                     
26641                     pos -= lines[i].length;
26642                     
26643                     break;
26644                 }
26645                 
26646                 if(!e.shiftKey){
26647                     this.el.dom.setSelectionRange(pos, pos);
26648                     return;
26649                 }
26650                 
26651                 this.el.dom.selectionStart = pos;
26652                 this.el.dom.selectionEnd = curr;
26653             },
26654             
26655             "end" : function(e){
26656                 e.preventDefault();
26657                 
26658                 var curr = this.el.dom.selectionStart;
26659                 var lines = this.getValue().split("\n");
26660                 
26661                 if(!lines.length){
26662                     return;
26663                 }
26664                 
26665                 if(e.ctrlKey){
26666                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26667                     return;
26668                 }
26669                 
26670                 var pos = 0;
26671                 
26672                 for (var i = 0; i < lines.length;i++) {
26673                     
26674                     pos += lines[i].length;
26675                     
26676                     if(i != 0){
26677                         pos += 1;
26678                     }
26679                     
26680                     if(pos < curr){
26681                         continue;
26682                     }
26683                     
26684                     break;
26685                 }
26686                 
26687                 if(!e.shiftKey){
26688                     this.el.dom.setSelectionRange(pos, pos);
26689                     return;
26690                 }
26691                 
26692                 this.el.dom.selectionStart = curr;
26693                 this.el.dom.selectionEnd = pos;
26694             },
26695
26696             scope : this,
26697
26698             doRelay : function(foo, bar, hname){
26699                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26700             },
26701
26702             forceKeyDown: true
26703         });
26704         
26705 //        if(this.autosave && this.w){
26706 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26707 //        }
26708     },
26709
26710     // private
26711     onResize : function(w, h)
26712     {
26713         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26714         var ew = false;
26715         var eh = false;
26716         
26717         if(this.el ){
26718             if(typeof w == 'number'){
26719                 var aw = w - this.wrap.getFrameWidth('lr');
26720                 this.el.setWidth(this.adjustWidth('textarea', aw));
26721                 ew = aw;
26722             }
26723             if(typeof h == 'number'){
26724                 var tbh = 0;
26725                 for (var i =0; i < this.toolbars.length;i++) {
26726                     // fixme - ask toolbars for heights?
26727                     tbh += this.toolbars[i].tb.el.getHeight();
26728                     if (this.toolbars[i].footer) {
26729                         tbh += this.toolbars[i].footer.el.getHeight();
26730                     }
26731                 }
26732                 
26733                 
26734                 
26735                 
26736                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26737                 ah -= 5; // knock a few pixes off for look..
26738 //                Roo.log(ah);
26739                 this.el.setHeight(this.adjustWidth('textarea', ah));
26740                 var eh = ah;
26741             }
26742         }
26743         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26744         this.editorcore.onResize(ew,eh);
26745         
26746     },
26747
26748     /**
26749      * Toggles the editor between standard and source edit mode.
26750      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26751      */
26752     toggleSourceEdit : function(sourceEditMode)
26753     {
26754         this.editorcore.toggleSourceEdit(sourceEditMode);
26755         
26756         if(this.editorcore.sourceEditMode){
26757             Roo.log('editor - showing textarea');
26758             
26759 //            Roo.log('in');
26760 //            Roo.log(this.syncValue());
26761             this.editorcore.syncValue();
26762             this.el.removeClass('x-hidden');
26763             this.el.dom.removeAttribute('tabIndex');
26764             this.el.focus();
26765             
26766             for (var i = 0; i < this.toolbars.length; i++) {
26767                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26768                     this.toolbars[i].tb.hide();
26769                     this.toolbars[i].footer.hide();
26770                 }
26771             }
26772             
26773         }else{
26774             Roo.log('editor - hiding textarea');
26775 //            Roo.log('out')
26776 //            Roo.log(this.pushValue()); 
26777             this.editorcore.pushValue();
26778             
26779             this.el.addClass('x-hidden');
26780             this.el.dom.setAttribute('tabIndex', -1);
26781             
26782             for (var i = 0; i < this.toolbars.length; i++) {
26783                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26784                     this.toolbars[i].tb.show();
26785                     this.toolbars[i].footer.show();
26786                 }
26787             }
26788             
26789             //this.deferFocus();
26790         }
26791         
26792         this.setSize(this.wrap.getSize());
26793         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26794         
26795         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26796     },
26797  
26798     // private (for BoxComponent)
26799     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26800
26801     // private (for BoxComponent)
26802     getResizeEl : function(){
26803         return this.wrap;
26804     },
26805
26806     // private (for BoxComponent)
26807     getPositionEl : function(){
26808         return this.wrap;
26809     },
26810
26811     // private
26812     initEvents : function(){
26813         this.originalValue = this.getValue();
26814     },
26815
26816     /**
26817      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26818      * @method
26819      */
26820     markInvalid : Roo.emptyFn,
26821     /**
26822      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26823      * @method
26824      */
26825     clearInvalid : Roo.emptyFn,
26826
26827     setValue : function(v){
26828         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26829         this.editorcore.pushValue();
26830     },
26831
26832      
26833     // private
26834     deferFocus : function(){
26835         this.focus.defer(10, this);
26836     },
26837
26838     // doc'ed in Field
26839     focus : function(){
26840         this.editorcore.focus();
26841         
26842     },
26843       
26844
26845     // private
26846     onDestroy : function(){
26847         
26848         
26849         
26850         if(this.rendered){
26851             
26852             for (var i =0; i < this.toolbars.length;i++) {
26853                 // fixme - ask toolbars for heights?
26854                 this.toolbars[i].onDestroy();
26855             }
26856             
26857             this.wrap.dom.innerHTML = '';
26858             this.wrap.remove();
26859         }
26860     },
26861
26862     // private
26863     onFirstFocus : function(){
26864         //Roo.log("onFirstFocus");
26865         this.editorcore.onFirstFocus();
26866          for (var i =0; i < this.toolbars.length;i++) {
26867             this.toolbars[i].onFirstFocus();
26868         }
26869         
26870     },
26871     
26872     // private
26873     syncValue : function()
26874     {
26875         this.editorcore.syncValue();
26876     },
26877     
26878     pushValue : function()
26879     {
26880         this.editorcore.pushValue();
26881     },
26882     
26883     setStylesheets : function(stylesheets)
26884     {
26885         this.editorcore.setStylesheets(stylesheets);
26886     },
26887     
26888     removeStylesheets : function()
26889     {
26890         this.editorcore.removeStylesheets();
26891     }
26892      
26893     
26894     // hide stuff that is not compatible
26895     /**
26896      * @event blur
26897      * @hide
26898      */
26899     /**
26900      * @event change
26901      * @hide
26902      */
26903     /**
26904      * @event focus
26905      * @hide
26906      */
26907     /**
26908      * @event specialkey
26909      * @hide
26910      */
26911     /**
26912      * @cfg {String} fieldClass @hide
26913      */
26914     /**
26915      * @cfg {String} focusClass @hide
26916      */
26917     /**
26918      * @cfg {String} autoCreate @hide
26919      */
26920     /**
26921      * @cfg {String} inputType @hide
26922      */
26923     /**
26924      * @cfg {String} invalidClass @hide
26925      */
26926     /**
26927      * @cfg {String} invalidText @hide
26928      */
26929     /**
26930      * @cfg {String} msgFx @hide
26931      */
26932     /**
26933      * @cfg {String} validateOnBlur @hide
26934      */
26935 });
26936  
26937     // <script type="text/javascript">
26938 /*
26939  * Based on
26940  * Ext JS Library 1.1.1
26941  * Copyright(c) 2006-2007, Ext JS, LLC.
26942  *  
26943  
26944  */
26945
26946 /**
26947  * @class Roo.form.HtmlEditorToolbar1
26948  * Basic Toolbar
26949  * 
26950  * Usage:
26951  *
26952  new Roo.form.HtmlEditor({
26953     ....
26954     toolbars : [
26955         new Roo.form.HtmlEditorToolbar1({
26956             disable : { fonts: 1 , format: 1, ..., ... , ...],
26957             btns : [ .... ]
26958         })
26959     }
26960      
26961  * 
26962  * @cfg {Object} disable List of elements to disable..
26963  * @cfg {Array} btns List of additional buttons.
26964  * 
26965  * 
26966  * NEEDS Extra CSS? 
26967  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26968  */
26969  
26970 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26971 {
26972     
26973     Roo.apply(this, config);
26974     
26975     // default disabled, based on 'good practice'..
26976     this.disable = this.disable || {};
26977     Roo.applyIf(this.disable, {
26978         fontSize : true,
26979         colors : true,
26980         specialElements : true
26981     });
26982     
26983     
26984     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26985     // dont call parent... till later.
26986 }
26987
26988 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26989     
26990     tb: false,
26991     
26992     rendered: false,
26993     
26994     editor : false,
26995     editorcore : false,
26996     /**
26997      * @cfg {Object} disable  List of toolbar elements to disable
26998          
26999      */
27000     disable : false,
27001     
27002     
27003      /**
27004      * @cfg {String} createLinkText The default text for the create link prompt
27005      */
27006     createLinkText : 'Please enter the URL for the link:',
27007     /**
27008      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27009      */
27010     defaultLinkValue : 'http:/'+'/',
27011    
27012     
27013       /**
27014      * @cfg {Array} fontFamilies An array of available font families
27015      */
27016     fontFamilies : [
27017         'Arial',
27018         'Courier New',
27019         'Tahoma',
27020         'Times New Roman',
27021         'Verdana'
27022     ],
27023     
27024     specialChars : [
27025            "&#169;",
27026           "&#174;",     
27027           "&#8482;",    
27028           "&#163;" ,    
27029          // "&#8212;",    
27030           "&#8230;",    
27031           "&#247;" ,    
27032         //  "&#225;" ,     ?? a acute?
27033            "&#8364;"    , //Euro
27034        //   "&#8220;"    ,
27035         //  "&#8221;"    ,
27036         //  "&#8226;"    ,
27037           "&#176;"  //   , // degrees
27038
27039          // "&#233;"     , // e ecute
27040          // "&#250;"     , // u ecute?
27041     ],
27042     
27043     specialElements : [
27044         {
27045             text: "Insert Table",
27046             xtype: 'MenuItem',
27047             xns : Roo.Menu,
27048             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27049                 
27050         },
27051         {    
27052             text: "Insert Image",
27053             xtype: 'MenuItem',
27054             xns : Roo.Menu,
27055             ihtml : '<img src="about:blank"/>'
27056             
27057         }
27058         
27059          
27060     ],
27061     
27062     
27063     inputElements : [ 
27064             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27065             "input:submit", "input:button", "select", "textarea", "label" ],
27066     formats : [
27067         ["p"] ,  
27068         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27069         ["pre"],[ "code"], 
27070         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27071         ['div'],['span']
27072     ],
27073     
27074     cleanStyles : [
27075         "font-size"
27076     ],
27077      /**
27078      * @cfg {String} defaultFont default font to use.
27079      */
27080     defaultFont: 'tahoma',
27081    
27082     fontSelect : false,
27083     
27084     
27085     formatCombo : false,
27086     
27087     init : function(editor)
27088     {
27089         this.editor = editor;
27090         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27091         var editorcore = this.editorcore;
27092         
27093         var _t = this;
27094         
27095         var fid = editorcore.frameId;
27096         var etb = this;
27097         function btn(id, toggle, handler){
27098             var xid = fid + '-'+ id ;
27099             return {
27100                 id : xid,
27101                 cmd : id,
27102                 cls : 'x-btn-icon x-edit-'+id,
27103                 enableToggle:toggle !== false,
27104                 scope: _t, // was editor...
27105                 handler:handler||_t.relayBtnCmd,
27106                 clickEvent:'mousedown',
27107                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27108                 tabIndex:-1
27109             };
27110         }
27111         
27112         
27113         
27114         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27115         this.tb = tb;
27116          // stop form submits
27117         tb.el.on('click', function(e){
27118             e.preventDefault(); // what does this do?
27119         });
27120
27121         if(!this.disable.font) { // && !Roo.isSafari){
27122             /* why no safari for fonts 
27123             editor.fontSelect = tb.el.createChild({
27124                 tag:'select',
27125                 tabIndex: -1,
27126                 cls:'x-font-select',
27127                 html: this.createFontOptions()
27128             });
27129             
27130             editor.fontSelect.on('change', function(){
27131                 var font = editor.fontSelect.dom.value;
27132                 editor.relayCmd('fontname', font);
27133                 editor.deferFocus();
27134             }, editor);
27135             
27136             tb.add(
27137                 editor.fontSelect.dom,
27138                 '-'
27139             );
27140             */
27141             
27142         };
27143         if(!this.disable.formats){
27144             this.formatCombo = new Roo.form.ComboBox({
27145                 store: new Roo.data.SimpleStore({
27146                     id : 'tag',
27147                     fields: ['tag'],
27148                     data : this.formats // from states.js
27149                 }),
27150                 blockFocus : true,
27151                 name : '',
27152                 //autoCreate : {tag: "div",  size: "20"},
27153                 displayField:'tag',
27154                 typeAhead: false,
27155                 mode: 'local',
27156                 editable : false,
27157                 triggerAction: 'all',
27158                 emptyText:'Add tag',
27159                 selectOnFocus:true,
27160                 width:135,
27161                 listeners : {
27162                     'select': function(c, r, i) {
27163                         editorcore.insertTag(r.get('tag'));
27164                         editor.focus();
27165                     }
27166                 }
27167
27168             });
27169             tb.addField(this.formatCombo);
27170             
27171         }
27172         
27173         if(!this.disable.format){
27174             tb.add(
27175                 btn('bold'),
27176                 btn('italic'),
27177                 btn('underline')
27178             );
27179         };
27180         if(!this.disable.fontSize){
27181             tb.add(
27182                 '-',
27183                 
27184                 
27185                 btn('increasefontsize', false, editorcore.adjustFont),
27186                 btn('decreasefontsize', false, editorcore.adjustFont)
27187             );
27188         };
27189         
27190         
27191         if(!this.disable.colors){
27192             tb.add(
27193                 '-', {
27194                     id:editorcore.frameId +'-forecolor',
27195                     cls:'x-btn-icon x-edit-forecolor',
27196                     clickEvent:'mousedown',
27197                     tooltip: this.buttonTips['forecolor'] || undefined,
27198                     tabIndex:-1,
27199                     menu : new Roo.menu.ColorMenu({
27200                         allowReselect: true,
27201                         focus: Roo.emptyFn,
27202                         value:'000000',
27203                         plain:true,
27204                         selectHandler: function(cp, color){
27205                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27206                             editor.deferFocus();
27207                         },
27208                         scope: editorcore,
27209                         clickEvent:'mousedown'
27210                     })
27211                 }, {
27212                     id:editorcore.frameId +'backcolor',
27213                     cls:'x-btn-icon x-edit-backcolor',
27214                     clickEvent:'mousedown',
27215                     tooltip: this.buttonTips['backcolor'] || undefined,
27216                     tabIndex:-1,
27217                     menu : new Roo.menu.ColorMenu({
27218                         focus: Roo.emptyFn,
27219                         value:'FFFFFF',
27220                         plain:true,
27221                         allowReselect: true,
27222                         selectHandler: function(cp, color){
27223                             if(Roo.isGecko){
27224                                 editorcore.execCmd('useCSS', false);
27225                                 editorcore.execCmd('hilitecolor', color);
27226                                 editorcore.execCmd('useCSS', true);
27227                                 editor.deferFocus();
27228                             }else{
27229                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27230                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27231                                 editor.deferFocus();
27232                             }
27233                         },
27234                         scope:editorcore,
27235                         clickEvent:'mousedown'
27236                     })
27237                 }
27238             );
27239         };
27240         // now add all the items...
27241         
27242
27243         if(!this.disable.alignments){
27244             tb.add(
27245                 '-',
27246                 btn('justifyleft'),
27247                 btn('justifycenter'),
27248                 btn('justifyright')
27249             );
27250         };
27251
27252         //if(!Roo.isSafari){
27253             if(!this.disable.links){
27254                 tb.add(
27255                     '-',
27256                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27257                 );
27258             };
27259
27260             if(!this.disable.lists){
27261                 tb.add(
27262                     '-',
27263                     btn('insertorderedlist'),
27264                     btn('insertunorderedlist')
27265                 );
27266             }
27267             if(!this.disable.sourceEdit){
27268                 tb.add(
27269                     '-',
27270                     btn('sourceedit', true, function(btn){
27271                         this.toggleSourceEdit(btn.pressed);
27272                     })
27273                 );
27274             }
27275         //}
27276         
27277         var smenu = { };
27278         // special menu.. - needs to be tidied up..
27279         if (!this.disable.special) {
27280             smenu = {
27281                 text: "&#169;",
27282                 cls: 'x-edit-none',
27283                 
27284                 menu : {
27285                     items : []
27286                 }
27287             };
27288             for (var i =0; i < this.specialChars.length; i++) {
27289                 smenu.menu.items.push({
27290                     
27291                     html: this.specialChars[i],
27292                     handler: function(a,b) {
27293                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27294                         //editor.insertAtCursor(a.html);
27295                         
27296                     },
27297                     tabIndex:-1
27298                 });
27299             }
27300             
27301             
27302             tb.add(smenu);
27303             
27304             
27305         }
27306         
27307         var cmenu = { };
27308         if (!this.disable.cleanStyles) {
27309             cmenu = {
27310                 cls: 'x-btn-icon x-btn-clear',
27311                 
27312                 menu : {
27313                     items : []
27314                 }
27315             };
27316             for (var i =0; i < this.cleanStyles.length; i++) {
27317                 cmenu.menu.items.push({
27318                     actiontype : this.cleanStyles[i],
27319                     html: 'Remove ' + this.cleanStyles[i],
27320                     handler: function(a,b) {
27321 //                        Roo.log(a);
27322 //                        Roo.log(b);
27323                         var c = Roo.get(editorcore.doc.body);
27324                         c.select('[style]').each(function(s) {
27325                             s.dom.style.removeProperty(a.actiontype);
27326                         });
27327                         editorcore.syncValue();
27328                     },
27329                     tabIndex:-1
27330                 });
27331             }
27332              cmenu.menu.items.push({
27333                 actiontype : 'tablewidths',
27334                 html: 'Remove Table Widths',
27335                 handler: function(a,b) {
27336                     editorcore.cleanTableWidths();
27337                     editorcore.syncValue();
27338                 },
27339                 tabIndex:-1
27340             });
27341             cmenu.menu.items.push({
27342                 actiontype : 'word',
27343                 html: 'Remove MS Word Formating',
27344                 handler: function(a,b) {
27345                     editorcore.cleanWord();
27346                     editorcore.syncValue();
27347                 },
27348                 tabIndex:-1
27349             });
27350             
27351             cmenu.menu.items.push({
27352                 actiontype : 'all',
27353                 html: 'Remove All Styles',
27354                 handler: function(a,b) {
27355                     
27356                     var c = Roo.get(editorcore.doc.body);
27357                     c.select('[style]').each(function(s) {
27358                         s.dom.removeAttribute('style');
27359                     });
27360                     editorcore.syncValue();
27361                 },
27362                 tabIndex:-1
27363             });
27364             
27365             cmenu.menu.items.push({
27366                 actiontype : 'all',
27367                 html: 'Remove All CSS Classes',
27368                 handler: function(a,b) {
27369                     
27370                     var c = Roo.get(editorcore.doc.body);
27371                     c.select('[class]').each(function(s) {
27372                         s.dom.className = '';
27373                     });
27374                     editorcore.syncValue();
27375                 },
27376                 tabIndex:-1
27377             });
27378             
27379              cmenu.menu.items.push({
27380                 actiontype : 'tidy',
27381                 html: 'Tidy HTML Source',
27382                 handler: function(a,b) {
27383                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27384                     editorcore.syncValue();
27385                 },
27386                 tabIndex:-1
27387             });
27388             
27389             
27390             tb.add(cmenu);
27391         }
27392          
27393         if (!this.disable.specialElements) {
27394             var semenu = {
27395                 text: "Other;",
27396                 cls: 'x-edit-none',
27397                 menu : {
27398                     items : []
27399                 }
27400             };
27401             for (var i =0; i < this.specialElements.length; i++) {
27402                 semenu.menu.items.push(
27403                     Roo.apply({ 
27404                         handler: function(a,b) {
27405                             editor.insertAtCursor(this.ihtml);
27406                         }
27407                     }, this.specialElements[i])
27408                 );
27409                     
27410             }
27411             
27412             tb.add(semenu);
27413             
27414             
27415         }
27416          
27417         
27418         if (this.btns) {
27419             for(var i =0; i< this.btns.length;i++) {
27420                 var b = Roo.factory(this.btns[i],Roo.form);
27421                 b.cls =  'x-edit-none';
27422                 
27423                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27424                     b.cls += ' x-init-enable';
27425                 }
27426                 
27427                 b.scope = editorcore;
27428                 tb.add(b);
27429             }
27430         
27431         }
27432         
27433         
27434         
27435         // disable everything...
27436         
27437         this.tb.items.each(function(item){
27438             
27439            if(
27440                 item.id != editorcore.frameId+ '-sourceedit' && 
27441                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27442             ){
27443                 
27444                 item.disable();
27445             }
27446         });
27447         this.rendered = true;
27448         
27449         // the all the btns;
27450         editor.on('editorevent', this.updateToolbar, this);
27451         // other toolbars need to implement this..
27452         //editor.on('editmodechange', this.updateToolbar, this);
27453     },
27454     
27455     
27456     relayBtnCmd : function(btn) {
27457         this.editorcore.relayCmd(btn.cmd);
27458     },
27459     // private used internally
27460     createLink : function(){
27461         Roo.log("create link?");
27462         var url = prompt(this.createLinkText, this.defaultLinkValue);
27463         if(url && url != 'http:/'+'/'){
27464             this.editorcore.relayCmd('createlink', url);
27465         }
27466     },
27467
27468     
27469     /**
27470      * Protected method that will not generally be called directly. It triggers
27471      * a toolbar update by reading the markup state of the current selection in the editor.
27472      */
27473     updateToolbar: function(){
27474
27475         if(!this.editorcore.activated){
27476             this.editor.onFirstFocus();
27477             return;
27478         }
27479
27480         var btns = this.tb.items.map, 
27481             doc = this.editorcore.doc,
27482             frameId = this.editorcore.frameId;
27483
27484         if(!this.disable.font && !Roo.isSafari){
27485             /*
27486             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27487             if(name != this.fontSelect.dom.value){
27488                 this.fontSelect.dom.value = name;
27489             }
27490             */
27491         }
27492         if(!this.disable.format){
27493             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27494             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27495             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27496         }
27497         if(!this.disable.alignments){
27498             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27499             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27500             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27501         }
27502         if(!Roo.isSafari && !this.disable.lists){
27503             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27504             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27505         }
27506         
27507         var ans = this.editorcore.getAllAncestors();
27508         if (this.formatCombo) {
27509             
27510             
27511             var store = this.formatCombo.store;
27512             this.formatCombo.setValue("");
27513             for (var i =0; i < ans.length;i++) {
27514                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27515                     // select it..
27516                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27517                     break;
27518                 }
27519             }
27520         }
27521         
27522         
27523         
27524         // hides menus... - so this cant be on a menu...
27525         Roo.menu.MenuMgr.hideAll();
27526
27527         //this.editorsyncValue();
27528     },
27529    
27530     
27531     createFontOptions : function(){
27532         var buf = [], fs = this.fontFamilies, ff, lc;
27533         
27534         
27535         
27536         for(var i = 0, len = fs.length; i< len; i++){
27537             ff = fs[i];
27538             lc = ff.toLowerCase();
27539             buf.push(
27540                 '<option value="',lc,'" style="font-family:',ff,';"',
27541                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27542                     ff,
27543                 '</option>'
27544             );
27545         }
27546         return buf.join('');
27547     },
27548     
27549     toggleSourceEdit : function(sourceEditMode){
27550         
27551         Roo.log("toolbar toogle");
27552         if(sourceEditMode === undefined){
27553             sourceEditMode = !this.sourceEditMode;
27554         }
27555         this.sourceEditMode = sourceEditMode === true;
27556         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27557         // just toggle the button?
27558         if(btn.pressed !== this.sourceEditMode){
27559             btn.toggle(this.sourceEditMode);
27560             return;
27561         }
27562         
27563         if(sourceEditMode){
27564             Roo.log("disabling buttons");
27565             this.tb.items.each(function(item){
27566                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27567                     item.disable();
27568                 }
27569             });
27570           
27571         }else{
27572             Roo.log("enabling buttons");
27573             if(this.editorcore.initialized){
27574                 this.tb.items.each(function(item){
27575                     item.enable();
27576                 });
27577             }
27578             
27579         }
27580         Roo.log("calling toggole on editor");
27581         // tell the editor that it's been pressed..
27582         this.editor.toggleSourceEdit(sourceEditMode);
27583        
27584     },
27585      /**
27586      * Object collection of toolbar tooltips for the buttons in the editor. The key
27587      * is the command id associated with that button and the value is a valid QuickTips object.
27588      * For example:
27589 <pre><code>
27590 {
27591     bold : {
27592         title: 'Bold (Ctrl+B)',
27593         text: 'Make the selected text bold.',
27594         cls: 'x-html-editor-tip'
27595     },
27596     italic : {
27597         title: 'Italic (Ctrl+I)',
27598         text: 'Make the selected text italic.',
27599         cls: 'x-html-editor-tip'
27600     },
27601     ...
27602 </code></pre>
27603     * @type Object
27604      */
27605     buttonTips : {
27606         bold : {
27607             title: 'Bold (Ctrl+B)',
27608             text: 'Make the selected text bold.',
27609             cls: 'x-html-editor-tip'
27610         },
27611         italic : {
27612             title: 'Italic (Ctrl+I)',
27613             text: 'Make the selected text italic.',
27614             cls: 'x-html-editor-tip'
27615         },
27616         underline : {
27617             title: 'Underline (Ctrl+U)',
27618             text: 'Underline the selected text.',
27619             cls: 'x-html-editor-tip'
27620         },
27621         increasefontsize : {
27622             title: 'Grow Text',
27623             text: 'Increase the font size.',
27624             cls: 'x-html-editor-tip'
27625         },
27626         decreasefontsize : {
27627             title: 'Shrink Text',
27628             text: 'Decrease the font size.',
27629             cls: 'x-html-editor-tip'
27630         },
27631         backcolor : {
27632             title: 'Text Highlight Color',
27633             text: 'Change the background color of the selected text.',
27634             cls: 'x-html-editor-tip'
27635         },
27636         forecolor : {
27637             title: 'Font Color',
27638             text: 'Change the color of the selected text.',
27639             cls: 'x-html-editor-tip'
27640         },
27641         justifyleft : {
27642             title: 'Align Text Left',
27643             text: 'Align text to the left.',
27644             cls: 'x-html-editor-tip'
27645         },
27646         justifycenter : {
27647             title: 'Center Text',
27648             text: 'Center text in the editor.',
27649             cls: 'x-html-editor-tip'
27650         },
27651         justifyright : {
27652             title: 'Align Text Right',
27653             text: 'Align text to the right.',
27654             cls: 'x-html-editor-tip'
27655         },
27656         insertunorderedlist : {
27657             title: 'Bullet List',
27658             text: 'Start a bulleted list.',
27659             cls: 'x-html-editor-tip'
27660         },
27661         insertorderedlist : {
27662             title: 'Numbered List',
27663             text: 'Start a numbered list.',
27664             cls: 'x-html-editor-tip'
27665         },
27666         createlink : {
27667             title: 'Hyperlink',
27668             text: 'Make the selected text a hyperlink.',
27669             cls: 'x-html-editor-tip'
27670         },
27671         sourceedit : {
27672             title: 'Source Edit',
27673             text: 'Switch to source editing mode.',
27674             cls: 'x-html-editor-tip'
27675         }
27676     },
27677     // private
27678     onDestroy : function(){
27679         if(this.rendered){
27680             
27681             this.tb.items.each(function(item){
27682                 if(item.menu){
27683                     item.menu.removeAll();
27684                     if(item.menu.el){
27685                         item.menu.el.destroy();
27686                     }
27687                 }
27688                 item.destroy();
27689             });
27690              
27691         }
27692     },
27693     onFirstFocus: function() {
27694         this.tb.items.each(function(item){
27695            item.enable();
27696         });
27697     }
27698 });
27699
27700
27701
27702
27703 // <script type="text/javascript">
27704 /*
27705  * Based on
27706  * Ext JS Library 1.1.1
27707  * Copyright(c) 2006-2007, Ext JS, LLC.
27708  *  
27709  
27710  */
27711
27712  
27713 /**
27714  * @class Roo.form.HtmlEditor.ToolbarContext
27715  * Context Toolbar
27716  * 
27717  * Usage:
27718  *
27719  new Roo.form.HtmlEditor({
27720     ....
27721     toolbars : [
27722         { xtype: 'ToolbarStandard', styles : {} }
27723         { xtype: 'ToolbarContext', disable : {} }
27724     ]
27725 })
27726
27727      
27728  * 
27729  * @config : {Object} disable List of elements to disable.. (not done yet.)
27730  * @config : {Object} styles  Map of styles available.
27731  * 
27732  */
27733
27734 Roo.form.HtmlEditor.ToolbarContext = function(config)
27735 {
27736     
27737     Roo.apply(this, config);
27738     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27739     // dont call parent... till later.
27740     this.styles = this.styles || {};
27741 }
27742
27743  
27744
27745 Roo.form.HtmlEditor.ToolbarContext.types = {
27746     'IMG' : {
27747         width : {
27748             title: "Width",
27749             width: 40
27750         },
27751         height:  {
27752             title: "Height",
27753             width: 40
27754         },
27755         align: {
27756             title: "Align",
27757             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27758             width : 80
27759             
27760         },
27761         border: {
27762             title: "Border",
27763             width: 40
27764         },
27765         alt: {
27766             title: "Alt",
27767             width: 120
27768         },
27769         src : {
27770             title: "Src",
27771             width: 220
27772         }
27773         
27774     },
27775     'A' : {
27776         name : {
27777             title: "Name",
27778             width: 50
27779         },
27780         target:  {
27781             title: "Target",
27782             width: 120
27783         },
27784         href:  {
27785             title: "Href",
27786             width: 220
27787         } // border?
27788         
27789     },
27790     'TABLE' : {
27791         rows : {
27792             title: "Rows",
27793             width: 20
27794         },
27795         cols : {
27796             title: "Cols",
27797             width: 20
27798         },
27799         width : {
27800             title: "Width",
27801             width: 40
27802         },
27803         height : {
27804             title: "Height",
27805             width: 40
27806         },
27807         border : {
27808             title: "Border",
27809             width: 20
27810         }
27811     },
27812     'TD' : {
27813         width : {
27814             title: "Width",
27815             width: 40
27816         },
27817         height : {
27818             title: "Height",
27819             width: 40
27820         },   
27821         align: {
27822             title: "Align",
27823             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27824             width: 80
27825         },
27826         valign: {
27827             title: "Valign",
27828             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27829             width: 80
27830         },
27831         colspan: {
27832             title: "Colspan",
27833             width: 20
27834             
27835         },
27836          'font-family'  : {
27837             title : "Font",
27838             style : 'fontFamily',
27839             displayField: 'display',
27840             optname : 'font-family',
27841             width: 140
27842         }
27843     },
27844     'INPUT' : {
27845         name : {
27846             title: "name",
27847             width: 120
27848         },
27849         value : {
27850             title: "Value",
27851             width: 120
27852         },
27853         width : {
27854             title: "Width",
27855             width: 40
27856         }
27857     },
27858     'LABEL' : {
27859         'for' : {
27860             title: "For",
27861             width: 120
27862         }
27863     },
27864     'TEXTAREA' : {
27865           name : {
27866             title: "name",
27867             width: 120
27868         },
27869         rows : {
27870             title: "Rows",
27871             width: 20
27872         },
27873         cols : {
27874             title: "Cols",
27875             width: 20
27876         }
27877     },
27878     'SELECT' : {
27879         name : {
27880             title: "name",
27881             width: 120
27882         },
27883         selectoptions : {
27884             title: "Options",
27885             width: 200
27886         }
27887     },
27888     
27889     // should we really allow this??
27890     // should this just be 
27891     'BODY' : {
27892         title : {
27893             title: "Title",
27894             width: 200,
27895             disabled : true
27896         }
27897     },
27898     'SPAN' : {
27899         'font-family'  : {
27900             title : "Font",
27901             style : 'fontFamily',
27902             displayField: 'display',
27903             optname : 'font-family',
27904             width: 140
27905         }
27906     },
27907     'DIV' : {
27908         'font-family'  : {
27909             title : "Font",
27910             style : 'fontFamily',
27911             displayField: 'display',
27912             optname : 'font-family',
27913             width: 140
27914         }
27915     },
27916      'P' : {
27917         'font-family'  : {
27918             title : "Font",
27919             style : 'fontFamily',
27920             displayField: 'display',
27921             optname : 'font-family',
27922             width: 140
27923         }
27924     },
27925     
27926     '*' : {
27927         // empty..
27928     }
27929
27930 };
27931
27932 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27933 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27934
27935 Roo.form.HtmlEditor.ToolbarContext.options = {
27936         'font-family'  : [ 
27937                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27938                 [ 'Courier New', 'Courier New'],
27939                 [ 'Tahoma', 'Tahoma'],
27940                 [ 'Times New Roman,serif', 'Times'],
27941                 [ 'Verdana','Verdana' ]
27942         ]
27943 };
27944
27945 // fixme - these need to be configurable..
27946  
27947
27948 Roo.form.HtmlEditor.ToolbarContext.types
27949
27950
27951 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27952     
27953     tb: false,
27954     
27955     rendered: false,
27956     
27957     editor : false,
27958     editorcore : false,
27959     /**
27960      * @cfg {Object} disable  List of toolbar elements to disable
27961          
27962      */
27963     disable : false,
27964     /**
27965      * @cfg {Object} styles List of styles 
27966      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27967      *
27968      * These must be defined in the page, so they get rendered correctly..
27969      * .headline { }
27970      * TD.underline { }
27971      * 
27972      */
27973     styles : false,
27974     
27975     options: false,
27976     
27977     toolbars : false,
27978     
27979     init : function(editor)
27980     {
27981         this.editor = editor;
27982         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27983         var editorcore = this.editorcore;
27984         
27985         var fid = editorcore.frameId;
27986         var etb = this;
27987         function btn(id, toggle, handler){
27988             var xid = fid + '-'+ id ;
27989             return {
27990                 id : xid,
27991                 cmd : id,
27992                 cls : 'x-btn-icon x-edit-'+id,
27993                 enableToggle:toggle !== false,
27994                 scope: editorcore, // was editor...
27995                 handler:handler||editorcore.relayBtnCmd,
27996                 clickEvent:'mousedown',
27997                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27998                 tabIndex:-1
27999             };
28000         }
28001         // create a new element.
28002         var wdiv = editor.wrap.createChild({
28003                 tag: 'div'
28004             }, editor.wrap.dom.firstChild.nextSibling, true);
28005         
28006         // can we do this more than once??
28007         
28008          // stop form submits
28009       
28010  
28011         // disable everything...
28012         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28013         this.toolbars = {};
28014            
28015         for (var i in  ty) {
28016           
28017             this.toolbars[i] = this.buildToolbar(ty[i],i);
28018         }
28019         this.tb = this.toolbars.BODY;
28020         this.tb.el.show();
28021         this.buildFooter();
28022         this.footer.show();
28023         editor.on('hide', function( ) { this.footer.hide() }, this);
28024         editor.on('show', function( ) { this.footer.show() }, this);
28025         
28026          
28027         this.rendered = true;
28028         
28029         // the all the btns;
28030         editor.on('editorevent', this.updateToolbar, this);
28031         // other toolbars need to implement this..
28032         //editor.on('editmodechange', this.updateToolbar, this);
28033     },
28034     
28035     
28036     
28037     /**
28038      * Protected method that will not generally be called directly. It triggers
28039      * a toolbar update by reading the markup state of the current selection in the editor.
28040      *
28041      * Note you can force an update by calling on('editorevent', scope, false)
28042      */
28043     updateToolbar: function(editor,ev,sel){
28044
28045         //Roo.log(ev);
28046         // capture mouse up - this is handy for selecting images..
28047         // perhaps should go somewhere else...
28048         if(!this.editorcore.activated){
28049              this.editor.onFirstFocus();
28050             return;
28051         }
28052         
28053         
28054         
28055         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28056         // selectNode - might want to handle IE?
28057         if (ev &&
28058             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28059             ev.target && ev.target.tagName == 'IMG') {
28060             // they have click on an image...
28061             // let's see if we can change the selection...
28062             sel = ev.target;
28063          
28064               var nodeRange = sel.ownerDocument.createRange();
28065             try {
28066                 nodeRange.selectNode(sel);
28067             } catch (e) {
28068                 nodeRange.selectNodeContents(sel);
28069             }
28070             //nodeRange.collapse(true);
28071             var s = this.editorcore.win.getSelection();
28072             s.removeAllRanges();
28073             s.addRange(nodeRange);
28074         }  
28075         
28076       
28077         var updateFooter = sel ? false : true;
28078         
28079         
28080         var ans = this.editorcore.getAllAncestors();
28081         
28082         // pick
28083         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28084         
28085         if (!sel) { 
28086             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28087             sel = sel ? sel : this.editorcore.doc.body;
28088             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28089             
28090         }
28091         // pick a menu that exists..
28092         var tn = sel.tagName.toUpperCase();
28093         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28094         
28095         tn = sel.tagName.toUpperCase();
28096         
28097         var lastSel = this.tb.selectedNode
28098         
28099         this.tb.selectedNode = sel;
28100         
28101         // if current menu does not match..
28102         
28103         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28104                 
28105             this.tb.el.hide();
28106             ///console.log("show: " + tn);
28107             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28108             this.tb.el.show();
28109             // update name
28110             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28111             
28112             
28113             // update attributes
28114             if (this.tb.fields) {
28115                 this.tb.fields.each(function(e) {
28116                     if (e.stylename) {
28117                         e.setValue(sel.style[e.stylename]);
28118                         return;
28119                     } 
28120                    e.setValue(sel.getAttribute(e.attrname));
28121                 });
28122             }
28123             
28124             var hasStyles = false;
28125             for(var i in this.styles) {
28126                 hasStyles = true;
28127                 break;
28128             }
28129             
28130             // update styles
28131             if (hasStyles) { 
28132                 var st = this.tb.fields.item(0);
28133                 
28134                 st.store.removeAll();
28135                
28136                 
28137                 var cn = sel.className.split(/\s+/);
28138                 
28139                 var avs = [];
28140                 if (this.styles['*']) {
28141                     
28142                     Roo.each(this.styles['*'], function(v) {
28143                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28144                     });
28145                 }
28146                 if (this.styles[tn]) { 
28147                     Roo.each(this.styles[tn], function(v) {
28148                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28149                     });
28150                 }
28151                 
28152                 st.store.loadData(avs);
28153                 st.collapse();
28154                 st.setValue(cn);
28155             }
28156             // flag our selected Node.
28157             this.tb.selectedNode = sel;
28158            
28159            
28160             Roo.menu.MenuMgr.hideAll();
28161
28162         }
28163         
28164         if (!updateFooter) {
28165             //this.footDisp.dom.innerHTML = ''; 
28166             return;
28167         }
28168         // update the footer
28169         //
28170         var html = '';
28171         
28172         this.footerEls = ans.reverse();
28173         Roo.each(this.footerEls, function(a,i) {
28174             if (!a) { return; }
28175             html += html.length ? ' &gt; '  :  '';
28176             
28177             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28178             
28179         });
28180        
28181         // 
28182         var sz = this.footDisp.up('td').getSize();
28183         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28184         this.footDisp.dom.style.marginLeft = '5px';
28185         
28186         this.footDisp.dom.style.overflow = 'hidden';
28187         
28188         this.footDisp.dom.innerHTML = html;
28189             
28190         //this.editorsyncValue();
28191     },
28192      
28193     
28194    
28195        
28196     // private
28197     onDestroy : function(){
28198         if(this.rendered){
28199             
28200             this.tb.items.each(function(item){
28201                 if(item.menu){
28202                     item.menu.removeAll();
28203                     if(item.menu.el){
28204                         item.menu.el.destroy();
28205                     }
28206                 }
28207                 item.destroy();
28208             });
28209              
28210         }
28211     },
28212     onFirstFocus: function() {
28213         // need to do this for all the toolbars..
28214         this.tb.items.each(function(item){
28215            item.enable();
28216         });
28217     },
28218     buildToolbar: function(tlist, nm)
28219     {
28220         var editor = this.editor;
28221         var editorcore = this.editorcore;
28222          // create a new element.
28223         var wdiv = editor.wrap.createChild({
28224                 tag: 'div'
28225             }, editor.wrap.dom.firstChild.nextSibling, true);
28226         
28227        
28228         var tb = new Roo.Toolbar(wdiv);
28229         // add the name..
28230         
28231         tb.add(nm+ ":&nbsp;");
28232         
28233         var styles = [];
28234         for(var i in this.styles) {
28235             styles.push(i);
28236         }
28237         
28238         // styles...
28239         if (styles && styles.length) {
28240             
28241             // this needs a multi-select checkbox...
28242             tb.addField( new Roo.form.ComboBox({
28243                 store: new Roo.data.SimpleStore({
28244                     id : 'val',
28245                     fields: ['val', 'selected'],
28246                     data : [] 
28247                 }),
28248                 name : '-roo-edit-className',
28249                 attrname : 'className',
28250                 displayField: 'val',
28251                 typeAhead: false,
28252                 mode: 'local',
28253                 editable : false,
28254                 triggerAction: 'all',
28255                 emptyText:'Select Style',
28256                 selectOnFocus:true,
28257                 width: 130,
28258                 listeners : {
28259                     'select': function(c, r, i) {
28260                         // initial support only for on class per el..
28261                         tb.selectedNode.className =  r ? r.get('val') : '';
28262                         editorcore.syncValue();
28263                     }
28264                 }
28265     
28266             }));
28267         }
28268         
28269         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28270         var tbops = tbc.options;
28271         
28272         for (var i in tlist) {
28273             
28274             var item = tlist[i];
28275             tb.add(item.title + ":&nbsp;");
28276             
28277             
28278             //optname == used so you can configure the options available..
28279             var opts = item.opts ? item.opts : false;
28280             if (item.optname) {
28281                 opts = tbops[item.optname];
28282            
28283             }
28284             
28285             if (opts) {
28286                 // opts == pulldown..
28287                 tb.addField( new Roo.form.ComboBox({
28288                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28289                         id : 'val',
28290                         fields: ['val', 'display'],
28291                         data : opts  
28292                     }),
28293                     name : '-roo-edit-' + i,
28294                     attrname : i,
28295                     stylename : item.style ? item.style : false,
28296                     displayField: item.displayField ? item.displayField : 'val',
28297                     valueField :  'val',
28298                     typeAhead: false,
28299                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28300                     editable : false,
28301                     triggerAction: 'all',
28302                     emptyText:'Select',
28303                     selectOnFocus:true,
28304                     width: item.width ? item.width  : 130,
28305                     listeners : {
28306                         'select': function(c, r, i) {
28307                             if (c.stylename) {
28308                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28309                                 return;
28310                             }
28311                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28312                         }
28313                     }
28314
28315                 }));
28316                 continue;
28317                     
28318                  
28319                 
28320                 tb.addField( new Roo.form.TextField({
28321                     name: i,
28322                     width: 100,
28323                     //allowBlank:false,
28324                     value: ''
28325                 }));
28326                 continue;
28327             }
28328             tb.addField( new Roo.form.TextField({
28329                 name: '-roo-edit-' + i,
28330                 attrname : i,
28331                 
28332                 width: item.width,
28333                 //allowBlank:true,
28334                 value: '',
28335                 listeners: {
28336                     'change' : function(f, nv, ov) {
28337                         tb.selectedNode.setAttribute(f.attrname, nv);
28338                     }
28339                 }
28340             }));
28341              
28342         }
28343         
28344         var _this = this;
28345         
28346         if(nm == 'BODY'){
28347             tb.addSeparator();
28348         
28349             tb.addButton( {
28350                 text: 'Stylesheets',
28351
28352                 listeners : {
28353                     click : function ()
28354                     {
28355                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28356                     }
28357                 }
28358             });
28359         }
28360         
28361         tb.addFill();
28362         tb.addButton( {
28363             text: 'Remove Tag',
28364     
28365             listeners : {
28366                 click : function ()
28367                 {
28368                     // remove
28369                     // undo does not work.
28370                      
28371                     var sn = tb.selectedNode;
28372                     
28373                     var pn = sn.parentNode;
28374                     
28375                     var stn =  sn.childNodes[0];
28376                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28377                     while (sn.childNodes.length) {
28378                         var node = sn.childNodes[0];
28379                         sn.removeChild(node);
28380                         //Roo.log(node);
28381                         pn.insertBefore(node, sn);
28382                         
28383                     }
28384                     pn.removeChild(sn);
28385                     var range = editorcore.createRange();
28386         
28387                     range.setStart(stn,0);
28388                     range.setEnd(en,0); //????
28389                     //range.selectNode(sel);
28390                     
28391                     
28392                     var selection = editorcore.getSelection();
28393                     selection.removeAllRanges();
28394                     selection.addRange(range);
28395                     
28396                     
28397                     
28398                     //_this.updateToolbar(null, null, pn);
28399                     _this.updateToolbar(null, null, null);
28400                     _this.footDisp.dom.innerHTML = ''; 
28401                 }
28402             }
28403             
28404                     
28405                 
28406             
28407         });
28408         
28409         
28410         tb.el.on('click', function(e){
28411             e.preventDefault(); // what does this do?
28412         });
28413         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28414         tb.el.hide();
28415         tb.name = nm;
28416         // dont need to disable them... as they will get hidden
28417         return tb;
28418          
28419         
28420     },
28421     buildFooter : function()
28422     {
28423         
28424         var fel = this.editor.wrap.createChild();
28425         this.footer = new Roo.Toolbar(fel);
28426         // toolbar has scrolly on left / right?
28427         var footDisp= new Roo.Toolbar.Fill();
28428         var _t = this;
28429         this.footer.add(
28430             {
28431                 text : '&lt;',
28432                 xtype: 'Button',
28433                 handler : function() {
28434                     _t.footDisp.scrollTo('left',0,true)
28435                 }
28436             }
28437         );
28438         this.footer.add( footDisp );
28439         this.footer.add( 
28440             {
28441                 text : '&gt;',
28442                 xtype: 'Button',
28443                 handler : function() {
28444                     // no animation..
28445                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28446                 }
28447             }
28448         );
28449         var fel = Roo.get(footDisp.el);
28450         fel.addClass('x-editor-context');
28451         this.footDispWrap = fel; 
28452         this.footDispWrap.overflow  = 'hidden';
28453         
28454         this.footDisp = fel.createChild();
28455         this.footDispWrap.on('click', this.onContextClick, this)
28456         
28457         
28458     },
28459     onContextClick : function (ev,dom)
28460     {
28461         ev.preventDefault();
28462         var  cn = dom.className;
28463         //Roo.log(cn);
28464         if (!cn.match(/x-ed-loc-/)) {
28465             return;
28466         }
28467         var n = cn.split('-').pop();
28468         var ans = this.footerEls;
28469         var sel = ans[n];
28470         
28471          // pick
28472         var range = this.editorcore.createRange();
28473         
28474         range.selectNodeContents(sel);
28475         //range.selectNode(sel);
28476         
28477         
28478         var selection = this.editorcore.getSelection();
28479         selection.removeAllRanges();
28480         selection.addRange(range);
28481         
28482         
28483         
28484         this.updateToolbar(null, null, sel);
28485         
28486         
28487     }
28488     
28489     
28490     
28491     
28492     
28493 });
28494
28495
28496
28497
28498
28499 /*
28500  * Based on:
28501  * Ext JS Library 1.1.1
28502  * Copyright(c) 2006-2007, Ext JS, LLC.
28503  *
28504  * Originally Released Under LGPL - original licence link has changed is not relivant.
28505  *
28506  * Fork - LGPL
28507  * <script type="text/javascript">
28508  */
28509  
28510 /**
28511  * @class Roo.form.BasicForm
28512  * @extends Roo.util.Observable
28513  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28514  * @constructor
28515  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28516  * @param {Object} config Configuration options
28517  */
28518 Roo.form.BasicForm = function(el, config){
28519     this.allItems = [];
28520     this.childForms = [];
28521     Roo.apply(this, config);
28522     /*
28523      * The Roo.form.Field items in this form.
28524      * @type MixedCollection
28525      */
28526      
28527      
28528     this.items = new Roo.util.MixedCollection(false, function(o){
28529         return o.id || (o.id = Roo.id());
28530     });
28531     this.addEvents({
28532         /**
28533          * @event beforeaction
28534          * Fires before any action is performed. Return false to cancel the action.
28535          * @param {Form} this
28536          * @param {Action} action The action to be performed
28537          */
28538         beforeaction: true,
28539         /**
28540          * @event actionfailed
28541          * Fires when an action fails.
28542          * @param {Form} this
28543          * @param {Action} action The action that failed
28544          */
28545         actionfailed : true,
28546         /**
28547          * @event actioncomplete
28548          * Fires when an action is completed.
28549          * @param {Form} this
28550          * @param {Action} action The action that completed
28551          */
28552         actioncomplete : true
28553     });
28554     if(el){
28555         this.initEl(el);
28556     }
28557     Roo.form.BasicForm.superclass.constructor.call(this);
28558 };
28559
28560 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28561     /**
28562      * @cfg {String} method
28563      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28564      */
28565     /**
28566      * @cfg {DataReader} reader
28567      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28568      * This is optional as there is built-in support for processing JSON.
28569      */
28570     /**
28571      * @cfg {DataReader} errorReader
28572      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28573      * This is completely optional as there is built-in support for processing JSON.
28574      */
28575     /**
28576      * @cfg {String} url
28577      * The URL to use for form actions if one isn't supplied in the action options.
28578      */
28579     /**
28580      * @cfg {Boolean} fileUpload
28581      * Set to true if this form is a file upload.
28582      */
28583      
28584     /**
28585      * @cfg {Object} baseParams
28586      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28587      */
28588      /**
28589      
28590     /**
28591      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28592      */
28593     timeout: 30,
28594
28595     // private
28596     activeAction : null,
28597
28598     /**
28599      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28600      * or setValues() data instead of when the form was first created.
28601      */
28602     trackResetOnLoad : false,
28603     
28604     
28605     /**
28606      * childForms - used for multi-tab forms
28607      * @type {Array}
28608      */
28609     childForms : false,
28610     
28611     /**
28612      * allItems - full list of fields.
28613      * @type {Array}
28614      */
28615     allItems : false,
28616     
28617     /**
28618      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28619      * element by passing it or its id or mask the form itself by passing in true.
28620      * @type Mixed
28621      */
28622     waitMsgTarget : false,
28623
28624     // private
28625     initEl : function(el){
28626         this.el = Roo.get(el);
28627         this.id = this.el.id || Roo.id();
28628         this.el.on('submit', this.onSubmit, this);
28629         this.el.addClass('x-form');
28630     },
28631
28632     // private
28633     onSubmit : function(e){
28634         e.stopEvent();
28635     },
28636
28637     /**
28638      * Returns true if client-side validation on the form is successful.
28639      * @return Boolean
28640      */
28641     isValid : function(){
28642         var valid = true;
28643         this.items.each(function(f){
28644            if(!f.validate()){
28645                valid = false;
28646            }
28647         });
28648         return valid;
28649     },
28650
28651     /**
28652      * Returns true if any fields in this form have changed since their original load.
28653      * @return Boolean
28654      */
28655     isDirty : function(){
28656         var dirty = false;
28657         this.items.each(function(f){
28658            if(f.isDirty()){
28659                dirty = true;
28660                return false;
28661            }
28662         });
28663         return dirty;
28664     },
28665
28666     /**
28667      * Performs a predefined action (submit or load) or custom actions you define on this form.
28668      * @param {String} actionName The name of the action type
28669      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28670      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28671      * accept other config options):
28672      * <pre>
28673 Property          Type             Description
28674 ----------------  ---------------  ----------------------------------------------------------------------------------
28675 url               String           The url for the action (defaults to the form's url)
28676 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28677 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28678 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28679                                    validate the form on the client (defaults to false)
28680      * </pre>
28681      * @return {BasicForm} this
28682      */
28683     doAction : function(action, options){
28684         if(typeof action == 'string'){
28685             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28686         }
28687         if(this.fireEvent('beforeaction', this, action) !== false){
28688             this.beforeAction(action);
28689             action.run.defer(100, action);
28690         }
28691         return this;
28692     },
28693
28694     /**
28695      * Shortcut to do a submit action.
28696      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28697      * @return {BasicForm} this
28698      */
28699     submit : function(options){
28700         this.doAction('submit', options);
28701         return this;
28702     },
28703
28704     /**
28705      * Shortcut to do a load action.
28706      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28707      * @return {BasicForm} this
28708      */
28709     load : function(options){
28710         this.doAction('load', options);
28711         return this;
28712     },
28713
28714     /**
28715      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28716      * @param {Record} record The record to edit
28717      * @return {BasicForm} this
28718      */
28719     updateRecord : function(record){
28720         record.beginEdit();
28721         var fs = record.fields;
28722         fs.each(function(f){
28723             var field = this.findField(f.name);
28724             if(field){
28725                 record.set(f.name, field.getValue());
28726             }
28727         }, this);
28728         record.endEdit();
28729         return this;
28730     },
28731
28732     /**
28733      * Loads an Roo.data.Record into this form.
28734      * @param {Record} record The record to load
28735      * @return {BasicForm} this
28736      */
28737     loadRecord : function(record){
28738         this.setValues(record.data);
28739         return this;
28740     },
28741
28742     // private
28743     beforeAction : function(action){
28744         var o = action.options;
28745         
28746        
28747         if(this.waitMsgTarget === true){
28748             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28749         }else if(this.waitMsgTarget){
28750             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28751             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28752         }else {
28753             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28754         }
28755          
28756     },
28757
28758     // private
28759     afterAction : function(action, success){
28760         this.activeAction = null;
28761         var o = action.options;
28762         
28763         if(this.waitMsgTarget === true){
28764             this.el.unmask();
28765         }else if(this.waitMsgTarget){
28766             this.waitMsgTarget.unmask();
28767         }else{
28768             Roo.MessageBox.updateProgress(1);
28769             Roo.MessageBox.hide();
28770         }
28771          
28772         if(success){
28773             if(o.reset){
28774                 this.reset();
28775             }
28776             Roo.callback(o.success, o.scope, [this, action]);
28777             this.fireEvent('actioncomplete', this, action);
28778             
28779         }else{
28780             
28781             // failure condition..
28782             // we have a scenario where updates need confirming.
28783             // eg. if a locking scenario exists..
28784             // we look for { errors : { needs_confirm : true }} in the response.
28785             if (
28786                 (typeof(action.result) != 'undefined')  &&
28787                 (typeof(action.result.errors) != 'undefined')  &&
28788                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28789            ){
28790                 var _t = this;
28791                 Roo.MessageBox.confirm(
28792                     "Change requires confirmation",
28793                     action.result.errorMsg,
28794                     function(r) {
28795                         if (r != 'yes') {
28796                             return;
28797                         }
28798                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28799                     }
28800                     
28801                 );
28802                 
28803                 
28804                 
28805                 return;
28806             }
28807             
28808             Roo.callback(o.failure, o.scope, [this, action]);
28809             // show an error message if no failed handler is set..
28810             if (!this.hasListener('actionfailed')) {
28811                 Roo.MessageBox.alert("Error",
28812                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28813                         action.result.errorMsg :
28814                         "Saving Failed, please check your entries or try again"
28815                 );
28816             }
28817             
28818             this.fireEvent('actionfailed', this, action);
28819         }
28820         
28821     },
28822
28823     /**
28824      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28825      * @param {String} id The value to search for
28826      * @return Field
28827      */
28828     findField : function(id){
28829         var field = this.items.get(id);
28830         if(!field){
28831             this.items.each(function(f){
28832                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28833                     field = f;
28834                     return false;
28835                 }
28836             });
28837         }
28838         return field || null;
28839     },
28840
28841     /**
28842      * Add a secondary form to this one, 
28843      * Used to provide tabbed forms. One form is primary, with hidden values 
28844      * which mirror the elements from the other forms.
28845      * 
28846      * @param {Roo.form.Form} form to add.
28847      * 
28848      */
28849     addForm : function(form)
28850     {
28851        
28852         if (this.childForms.indexOf(form) > -1) {
28853             // already added..
28854             return;
28855         }
28856         this.childForms.push(form);
28857         var n = '';
28858         Roo.each(form.allItems, function (fe) {
28859             
28860             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28861             if (this.findField(n)) { // already added..
28862                 return;
28863             }
28864             var add = new Roo.form.Hidden({
28865                 name : n
28866             });
28867             add.render(this.el);
28868             
28869             this.add( add );
28870         }, this);
28871         
28872     },
28873     /**
28874      * Mark fields in this form invalid in bulk.
28875      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28876      * @return {BasicForm} this
28877      */
28878     markInvalid : function(errors){
28879         if(errors instanceof Array){
28880             for(var i = 0, len = errors.length; i < len; i++){
28881                 var fieldError = errors[i];
28882                 var f = this.findField(fieldError.id);
28883                 if(f){
28884                     f.markInvalid(fieldError.msg);
28885                 }
28886             }
28887         }else{
28888             var field, id;
28889             for(id in errors){
28890                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28891                     field.markInvalid(errors[id]);
28892                 }
28893             }
28894         }
28895         Roo.each(this.childForms || [], function (f) {
28896             f.markInvalid(errors);
28897         });
28898         
28899         return this;
28900     },
28901
28902     /**
28903      * Set values for fields in this form in bulk.
28904      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28905      * @return {BasicForm} this
28906      */
28907     setValues : function(values){
28908         if(values instanceof Array){ // array of objects
28909             for(var i = 0, len = values.length; i < len; i++){
28910                 var v = values[i];
28911                 var f = this.findField(v.id);
28912                 if(f){
28913                     f.setValue(v.value);
28914                     if(this.trackResetOnLoad){
28915                         f.originalValue = f.getValue();
28916                     }
28917                 }
28918             }
28919         }else{ // object hash
28920             var field, id;
28921             for(id in values){
28922                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28923                     
28924                     if (field.setFromData && 
28925                         field.valueField && 
28926                         field.displayField &&
28927                         // combos' with local stores can 
28928                         // be queried via setValue()
28929                         // to set their value..
28930                         (field.store && !field.store.isLocal)
28931                         ) {
28932                         // it's a combo
28933                         var sd = { };
28934                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28935                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28936                         field.setFromData(sd);
28937                         
28938                     } else {
28939                         field.setValue(values[id]);
28940                     }
28941                     
28942                     
28943                     if(this.trackResetOnLoad){
28944                         field.originalValue = field.getValue();
28945                     }
28946                 }
28947             }
28948         }
28949          
28950         Roo.each(this.childForms || [], function (f) {
28951             f.setValues(values);
28952         });
28953                 
28954         return this;
28955     },
28956
28957     /**
28958      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28959      * they are returned as an array.
28960      * @param {Boolean} asString
28961      * @return {Object}
28962      */
28963     getValues : function(asString){
28964         if (this.childForms) {
28965             // copy values from the child forms
28966             Roo.each(this.childForms, function (f) {
28967                 this.setValues(f.getValues());
28968             }, this);
28969         }
28970         
28971         
28972         
28973         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28974         if(asString === true){
28975             return fs;
28976         }
28977         return Roo.urlDecode(fs);
28978     },
28979     
28980     /**
28981      * Returns the fields in this form as an object with key/value pairs. 
28982      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28983      * @return {Object}
28984      */
28985     getFieldValues : function(with_hidden)
28986     {
28987         if (this.childForms) {
28988             // copy values from the child forms
28989             // should this call getFieldValues - probably not as we do not currently copy
28990             // hidden fields when we generate..
28991             Roo.each(this.childForms, function (f) {
28992                 this.setValues(f.getValues());
28993             }, this);
28994         }
28995         
28996         var ret = {};
28997         this.items.each(function(f){
28998             if (!f.getName()) {
28999                 return;
29000             }
29001             var v = f.getValue();
29002             if (f.inputType =='radio') {
29003                 if (typeof(ret[f.getName()]) == 'undefined') {
29004                     ret[f.getName()] = ''; // empty..
29005                 }
29006                 
29007                 if (!f.el.dom.checked) {
29008                     return;
29009                     
29010                 }
29011                 v = f.el.dom.value;
29012                 
29013             }
29014             
29015             // not sure if this supported any more..
29016             if ((typeof(v) == 'object') && f.getRawValue) {
29017                 v = f.getRawValue() ; // dates..
29018             }
29019             // combo boxes where name != hiddenName...
29020             if (f.name != f.getName()) {
29021                 ret[f.name] = f.getRawValue();
29022             }
29023             ret[f.getName()] = v;
29024         });
29025         
29026         return ret;
29027     },
29028
29029     /**
29030      * Clears all invalid messages in this form.
29031      * @return {BasicForm} this
29032      */
29033     clearInvalid : function(){
29034         this.items.each(function(f){
29035            f.clearInvalid();
29036         });
29037         
29038         Roo.each(this.childForms || [], function (f) {
29039             f.clearInvalid();
29040         });
29041         
29042         
29043         return this;
29044     },
29045
29046     /**
29047      * Resets this form.
29048      * @return {BasicForm} this
29049      */
29050     reset : function(){
29051         this.items.each(function(f){
29052             f.reset();
29053         });
29054         
29055         Roo.each(this.childForms || [], function (f) {
29056             f.reset();
29057         });
29058        
29059         
29060         return this;
29061     },
29062
29063     /**
29064      * Add Roo.form components to this form.
29065      * @param {Field} field1
29066      * @param {Field} field2 (optional)
29067      * @param {Field} etc (optional)
29068      * @return {BasicForm} this
29069      */
29070     add : function(){
29071         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29072         return this;
29073     },
29074
29075
29076     /**
29077      * Removes a field from the items collection (does NOT remove its markup).
29078      * @param {Field} field
29079      * @return {BasicForm} this
29080      */
29081     remove : function(field){
29082         this.items.remove(field);
29083         return this;
29084     },
29085
29086     /**
29087      * Looks at the fields in this form, checks them for an id attribute,
29088      * and calls applyTo on the existing dom element with that id.
29089      * @return {BasicForm} this
29090      */
29091     render : function(){
29092         this.items.each(function(f){
29093             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29094                 f.applyTo(f.id);
29095             }
29096         });
29097         return this;
29098     },
29099
29100     /**
29101      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29102      * @param {Object} values
29103      * @return {BasicForm} this
29104      */
29105     applyToFields : function(o){
29106         this.items.each(function(f){
29107            Roo.apply(f, o);
29108         });
29109         return this;
29110     },
29111
29112     /**
29113      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29114      * @param {Object} values
29115      * @return {BasicForm} this
29116      */
29117     applyIfToFields : function(o){
29118         this.items.each(function(f){
29119            Roo.applyIf(f, o);
29120         });
29121         return this;
29122     }
29123 });
29124
29125 // back compat
29126 Roo.BasicForm = Roo.form.BasicForm;/*
29127  * Based on:
29128  * Ext JS Library 1.1.1
29129  * Copyright(c) 2006-2007, Ext JS, LLC.
29130  *
29131  * Originally Released Under LGPL - original licence link has changed is not relivant.
29132  *
29133  * Fork - LGPL
29134  * <script type="text/javascript">
29135  */
29136
29137 /**
29138  * @class Roo.form.Form
29139  * @extends Roo.form.BasicForm
29140  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29141  * @constructor
29142  * @param {Object} config Configuration options
29143  */
29144 Roo.form.Form = function(config){
29145     var xitems =  [];
29146     if (config.items) {
29147         xitems = config.items;
29148         delete config.items;
29149     }
29150    
29151     
29152     Roo.form.Form.superclass.constructor.call(this, null, config);
29153     this.url = this.url || this.action;
29154     if(!this.root){
29155         this.root = new Roo.form.Layout(Roo.applyIf({
29156             id: Roo.id()
29157         }, config));
29158     }
29159     this.active = this.root;
29160     /**
29161      * Array of all the buttons that have been added to this form via {@link addButton}
29162      * @type Array
29163      */
29164     this.buttons = [];
29165     this.allItems = [];
29166     this.addEvents({
29167         /**
29168          * @event clientvalidation
29169          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29170          * @param {Form} this
29171          * @param {Boolean} valid true if the form has passed client-side validation
29172          */
29173         clientvalidation: true,
29174         /**
29175          * @event rendered
29176          * Fires when the form is rendered
29177          * @param {Roo.form.Form} form
29178          */
29179         rendered : true
29180     });
29181     
29182     if (this.progressUrl) {
29183             // push a hidden field onto the list of fields..
29184             this.addxtype( {
29185                     xns: Roo.form, 
29186                     xtype : 'Hidden', 
29187                     name : 'UPLOAD_IDENTIFIER' 
29188             });
29189         }
29190         
29191     
29192     Roo.each(xitems, this.addxtype, this);
29193     
29194     
29195     
29196 };
29197
29198 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29199     /**
29200      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29201      */
29202     /**
29203      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29204      */
29205     /**
29206      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29207      */
29208     buttonAlign:'center',
29209
29210     /**
29211      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29212      */
29213     minButtonWidth:75,
29214
29215     /**
29216      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29217      * This property cascades to child containers if not set.
29218      */
29219     labelAlign:'left',
29220
29221     /**
29222      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29223      * fires a looping event with that state. This is required to bind buttons to the valid
29224      * state using the config value formBind:true on the button.
29225      */
29226     monitorValid : false,
29227
29228     /**
29229      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29230      */
29231     monitorPoll : 200,
29232     
29233     /**
29234      * @cfg {String} progressUrl - Url to return progress data 
29235      */
29236     
29237     progressUrl : false,
29238   
29239     /**
29240      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29241      * fields are added and the column is closed. If no fields are passed the column remains open
29242      * until end() is called.
29243      * @param {Object} config The config to pass to the column
29244      * @param {Field} field1 (optional)
29245      * @param {Field} field2 (optional)
29246      * @param {Field} etc (optional)
29247      * @return Column The column container object
29248      */
29249     column : function(c){
29250         var col = new Roo.form.Column(c);
29251         this.start(col);
29252         if(arguments.length > 1){ // duplicate code required because of Opera
29253             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29254             this.end();
29255         }
29256         return col;
29257     },
29258
29259     /**
29260      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29261      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29262      * until end() is called.
29263      * @param {Object} config The config to pass to the fieldset
29264      * @param {Field} field1 (optional)
29265      * @param {Field} field2 (optional)
29266      * @param {Field} etc (optional)
29267      * @return FieldSet The fieldset container object
29268      */
29269     fieldset : function(c){
29270         var fs = new Roo.form.FieldSet(c);
29271         this.start(fs);
29272         if(arguments.length > 1){ // duplicate code required because of Opera
29273             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29274             this.end();
29275         }
29276         return fs;
29277     },
29278
29279     /**
29280      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29281      * fields are added and the container is closed. If no fields are passed the container remains open
29282      * until end() is called.
29283      * @param {Object} config The config to pass to the Layout
29284      * @param {Field} field1 (optional)
29285      * @param {Field} field2 (optional)
29286      * @param {Field} etc (optional)
29287      * @return Layout The container object
29288      */
29289     container : function(c){
29290         var l = new Roo.form.Layout(c);
29291         this.start(l);
29292         if(arguments.length > 1){ // duplicate code required because of Opera
29293             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29294             this.end();
29295         }
29296         return l;
29297     },
29298
29299     /**
29300      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29301      * @param {Object} container A Roo.form.Layout or subclass of Layout
29302      * @return {Form} this
29303      */
29304     start : function(c){
29305         // cascade label info
29306         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29307         this.active.stack.push(c);
29308         c.ownerCt = this.active;
29309         this.active = c;
29310         return this;
29311     },
29312
29313     /**
29314      * Closes the current open container
29315      * @return {Form} this
29316      */
29317     end : function(){
29318         if(this.active == this.root){
29319             return this;
29320         }
29321         this.active = this.active.ownerCt;
29322         return this;
29323     },
29324
29325     /**
29326      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29327      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29328      * as the label of the field.
29329      * @param {Field} field1
29330      * @param {Field} field2 (optional)
29331      * @param {Field} etc. (optional)
29332      * @return {Form} this
29333      */
29334     add : function(){
29335         this.active.stack.push.apply(this.active.stack, arguments);
29336         this.allItems.push.apply(this.allItems,arguments);
29337         var r = [];
29338         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29339             if(a[i].isFormField){
29340                 r.push(a[i]);
29341             }
29342         }
29343         if(r.length > 0){
29344             Roo.form.Form.superclass.add.apply(this, r);
29345         }
29346         return this;
29347     },
29348     
29349
29350     
29351     
29352     
29353      /**
29354      * Find any element that has been added to a form, using it's ID or name
29355      * This can include framesets, columns etc. along with regular fields..
29356      * @param {String} id - id or name to find.
29357      
29358      * @return {Element} e - or false if nothing found.
29359      */
29360     findbyId : function(id)
29361     {
29362         var ret = false;
29363         if (!id) {
29364             return ret;
29365         }
29366         Roo.each(this.allItems, function(f){
29367             if (f.id == id || f.name == id ){
29368                 ret = f;
29369                 return false;
29370             }
29371         });
29372         return ret;
29373     },
29374
29375     
29376     
29377     /**
29378      * Render this form into the passed container. This should only be called once!
29379      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29380      * @return {Form} this
29381      */
29382     render : function(ct)
29383     {
29384         
29385         
29386         
29387         ct = Roo.get(ct);
29388         var o = this.autoCreate || {
29389             tag: 'form',
29390             method : this.method || 'POST',
29391             id : this.id || Roo.id()
29392         };
29393         this.initEl(ct.createChild(o));
29394
29395         this.root.render(this.el);
29396         
29397        
29398              
29399         this.items.each(function(f){
29400             f.render('x-form-el-'+f.id);
29401         });
29402
29403         if(this.buttons.length > 0){
29404             // tables are required to maintain order and for correct IE layout
29405             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29406                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29407                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29408             }}, null, true);
29409             var tr = tb.getElementsByTagName('tr')[0];
29410             for(var i = 0, len = this.buttons.length; i < len; i++) {
29411                 var b = this.buttons[i];
29412                 var td = document.createElement('td');
29413                 td.className = 'x-form-btn-td';
29414                 b.render(tr.appendChild(td));
29415             }
29416         }
29417         if(this.monitorValid){ // initialize after render
29418             this.startMonitoring();
29419         }
29420         this.fireEvent('rendered', this);
29421         return this;
29422     },
29423
29424     /**
29425      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29426      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29427      * object or a valid Roo.DomHelper element config
29428      * @param {Function} handler The function called when the button is clicked
29429      * @param {Object} scope (optional) The scope of the handler function
29430      * @return {Roo.Button}
29431      */
29432     addButton : function(config, handler, scope){
29433         var bc = {
29434             handler: handler,
29435             scope: scope,
29436             minWidth: this.minButtonWidth,
29437             hideParent:true
29438         };
29439         if(typeof config == "string"){
29440             bc.text = config;
29441         }else{
29442             Roo.apply(bc, config);
29443         }
29444         var btn = new Roo.Button(null, bc);
29445         this.buttons.push(btn);
29446         return btn;
29447     },
29448
29449      /**
29450      * Adds a series of form elements (using the xtype property as the factory method.
29451      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29452      * @param {Object} config 
29453      */
29454     
29455     addxtype : function()
29456     {
29457         var ar = Array.prototype.slice.call(arguments, 0);
29458         var ret = false;
29459         for(var i = 0; i < ar.length; i++) {
29460             if (!ar[i]) {
29461                 continue; // skip -- if this happends something invalid got sent, we 
29462                 // should ignore it, as basically that interface element will not show up
29463                 // and that should be pretty obvious!!
29464             }
29465             
29466             if (Roo.form[ar[i].xtype]) {
29467                 ar[i].form = this;
29468                 var fe = Roo.factory(ar[i], Roo.form);
29469                 if (!ret) {
29470                     ret = fe;
29471                 }
29472                 fe.form = this;
29473                 if (fe.store) {
29474                     fe.store.form = this;
29475                 }
29476                 if (fe.isLayout) {  
29477                          
29478                     this.start(fe);
29479                     this.allItems.push(fe);
29480                     if (fe.items && fe.addxtype) {
29481                         fe.addxtype.apply(fe, fe.items);
29482                         delete fe.items;
29483                     }
29484                      this.end();
29485                     continue;
29486                 }
29487                 
29488                 
29489                  
29490                 this.add(fe);
29491               //  console.log('adding ' + ar[i].xtype);
29492             }
29493             if (ar[i].xtype == 'Button') {  
29494                 //console.log('adding button');
29495                 //console.log(ar[i]);
29496                 this.addButton(ar[i]);
29497                 this.allItems.push(fe);
29498                 continue;
29499             }
29500             
29501             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29502                 alert('end is not supported on xtype any more, use items');
29503             //    this.end();
29504             //    //console.log('adding end');
29505             }
29506             
29507         }
29508         return ret;
29509     },
29510     
29511     /**
29512      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29513      * option "monitorValid"
29514      */
29515     startMonitoring : function(){
29516         if(!this.bound){
29517             this.bound = true;
29518             Roo.TaskMgr.start({
29519                 run : this.bindHandler,
29520                 interval : this.monitorPoll || 200,
29521                 scope: this
29522             });
29523         }
29524     },
29525
29526     /**
29527      * Stops monitoring of the valid state of this form
29528      */
29529     stopMonitoring : function(){
29530         this.bound = false;
29531     },
29532
29533     // private
29534     bindHandler : function(){
29535         if(!this.bound){
29536             return false; // stops binding
29537         }
29538         var valid = true;
29539         this.items.each(function(f){
29540             if(!f.isValid(true)){
29541                 valid = false;
29542                 return false;
29543             }
29544         });
29545         for(var i = 0, len = this.buttons.length; i < len; i++){
29546             var btn = this.buttons[i];
29547             if(btn.formBind === true && btn.disabled === valid){
29548                 btn.setDisabled(!valid);
29549             }
29550         }
29551         this.fireEvent('clientvalidation', this, valid);
29552     }
29553     
29554     
29555     
29556     
29557     
29558     
29559     
29560     
29561 });
29562
29563
29564 // back compat
29565 Roo.Form = Roo.form.Form;
29566 /*
29567  * Based on:
29568  * Ext JS Library 1.1.1
29569  * Copyright(c) 2006-2007, Ext JS, LLC.
29570  *
29571  * Originally Released Under LGPL - original licence link has changed is not relivant.
29572  *
29573  * Fork - LGPL
29574  * <script type="text/javascript">
29575  */
29576
29577 // as we use this in bootstrap.
29578 Roo.namespace('Roo.form');
29579  /**
29580  * @class Roo.form.Action
29581  * Internal Class used to handle form actions
29582  * @constructor
29583  * @param {Roo.form.BasicForm} el The form element or its id
29584  * @param {Object} config Configuration options
29585  */
29586
29587  
29588  
29589 // define the action interface
29590 Roo.form.Action = function(form, options){
29591     this.form = form;
29592     this.options = options || {};
29593 };
29594 /**
29595  * Client Validation Failed
29596  * @const 
29597  */
29598 Roo.form.Action.CLIENT_INVALID = 'client';
29599 /**
29600  * Server Validation Failed
29601  * @const 
29602  */
29603 Roo.form.Action.SERVER_INVALID = 'server';
29604  /**
29605  * Connect to Server Failed
29606  * @const 
29607  */
29608 Roo.form.Action.CONNECT_FAILURE = 'connect';
29609 /**
29610  * Reading Data from Server Failed
29611  * @const 
29612  */
29613 Roo.form.Action.LOAD_FAILURE = 'load';
29614
29615 Roo.form.Action.prototype = {
29616     type : 'default',
29617     failureType : undefined,
29618     response : undefined,
29619     result : undefined,
29620
29621     // interface method
29622     run : function(options){
29623
29624     },
29625
29626     // interface method
29627     success : function(response){
29628
29629     },
29630
29631     // interface method
29632     handleResponse : function(response){
29633
29634     },
29635
29636     // default connection failure
29637     failure : function(response){
29638         
29639         this.response = response;
29640         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29641         this.form.afterAction(this, false);
29642     },
29643
29644     processResponse : function(response){
29645         this.response = response;
29646         if(!response.responseText){
29647             return true;
29648         }
29649         this.result = this.handleResponse(response);
29650         return this.result;
29651     },
29652
29653     // utility functions used internally
29654     getUrl : function(appendParams){
29655         var url = this.options.url || this.form.url || this.form.el.dom.action;
29656         if(appendParams){
29657             var p = this.getParams();
29658             if(p){
29659                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29660             }
29661         }
29662         return url;
29663     },
29664
29665     getMethod : function(){
29666         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29667     },
29668
29669     getParams : function(){
29670         var bp = this.form.baseParams;
29671         var p = this.options.params;
29672         if(p){
29673             if(typeof p == "object"){
29674                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29675             }else if(typeof p == 'string' && bp){
29676                 p += '&' + Roo.urlEncode(bp);
29677             }
29678         }else if(bp){
29679             p = Roo.urlEncode(bp);
29680         }
29681         return p;
29682     },
29683
29684     createCallback : function(){
29685         return {
29686             success: this.success,
29687             failure: this.failure,
29688             scope: this,
29689             timeout: (this.form.timeout*1000),
29690             upload: this.form.fileUpload ? this.success : undefined
29691         };
29692     }
29693 };
29694
29695 Roo.form.Action.Submit = function(form, options){
29696     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29697 };
29698
29699 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29700     type : 'submit',
29701
29702     haveProgress : false,
29703     uploadComplete : false,
29704     
29705     // uploadProgress indicator.
29706     uploadProgress : function()
29707     {
29708         if (!this.form.progressUrl) {
29709             return;
29710         }
29711         
29712         if (!this.haveProgress) {
29713             Roo.MessageBox.progress("Uploading", "Uploading");
29714         }
29715         if (this.uploadComplete) {
29716            Roo.MessageBox.hide();
29717            return;
29718         }
29719         
29720         this.haveProgress = true;
29721    
29722         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29723         
29724         var c = new Roo.data.Connection();
29725         c.request({
29726             url : this.form.progressUrl,
29727             params: {
29728                 id : uid
29729             },
29730             method: 'GET',
29731             success : function(req){
29732                //console.log(data);
29733                 var rdata = false;
29734                 var edata;
29735                 try  {
29736                    rdata = Roo.decode(req.responseText)
29737                 } catch (e) {
29738                     Roo.log("Invalid data from server..");
29739                     Roo.log(edata);
29740                     return;
29741                 }
29742                 if (!rdata || !rdata.success) {
29743                     Roo.log(rdata);
29744                     Roo.MessageBox.alert(Roo.encode(rdata));
29745                     return;
29746                 }
29747                 var data = rdata.data;
29748                 
29749                 if (this.uploadComplete) {
29750                    Roo.MessageBox.hide();
29751                    return;
29752                 }
29753                    
29754                 if (data){
29755                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29756                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29757                     );
29758                 }
29759                 this.uploadProgress.defer(2000,this);
29760             },
29761        
29762             failure: function(data) {
29763                 Roo.log('progress url failed ');
29764                 Roo.log(data);
29765             },
29766             scope : this
29767         });
29768            
29769     },
29770     
29771     
29772     run : function()
29773     {
29774         // run get Values on the form, so it syncs any secondary forms.
29775         this.form.getValues();
29776         
29777         var o = this.options;
29778         var method = this.getMethod();
29779         var isPost = method == 'POST';
29780         if(o.clientValidation === false || this.form.isValid()){
29781             
29782             if (this.form.progressUrl) {
29783                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29784                     (new Date() * 1) + '' + Math.random());
29785                     
29786             } 
29787             
29788             
29789             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29790                 form:this.form.el.dom,
29791                 url:this.getUrl(!isPost),
29792                 method: method,
29793                 params:isPost ? this.getParams() : null,
29794                 isUpload: this.form.fileUpload
29795             }));
29796             
29797             this.uploadProgress();
29798
29799         }else if (o.clientValidation !== false){ // client validation failed
29800             this.failureType = Roo.form.Action.CLIENT_INVALID;
29801             this.form.afterAction(this, false);
29802         }
29803     },
29804
29805     success : function(response)
29806     {
29807         this.uploadComplete= true;
29808         if (this.haveProgress) {
29809             Roo.MessageBox.hide();
29810         }
29811         
29812         
29813         var result = this.processResponse(response);
29814         if(result === true || result.success){
29815             this.form.afterAction(this, true);
29816             return;
29817         }
29818         if(result.errors){
29819             this.form.markInvalid(result.errors);
29820             this.failureType = Roo.form.Action.SERVER_INVALID;
29821         }
29822         this.form.afterAction(this, false);
29823     },
29824     failure : function(response)
29825     {
29826         this.uploadComplete= true;
29827         if (this.haveProgress) {
29828             Roo.MessageBox.hide();
29829         }
29830         
29831         this.response = response;
29832         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29833         this.form.afterAction(this, false);
29834     },
29835     
29836     handleResponse : function(response){
29837         if(this.form.errorReader){
29838             var rs = this.form.errorReader.read(response);
29839             var errors = [];
29840             if(rs.records){
29841                 for(var i = 0, len = rs.records.length; i < len; i++) {
29842                     var r = rs.records[i];
29843                     errors[i] = r.data;
29844                 }
29845             }
29846             if(errors.length < 1){
29847                 errors = null;
29848             }
29849             return {
29850                 success : rs.success,
29851                 errors : errors
29852             };
29853         }
29854         var ret = false;
29855         try {
29856             ret = Roo.decode(response.responseText);
29857         } catch (e) {
29858             ret = {
29859                 success: false,
29860                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29861                 errors : []
29862             };
29863         }
29864         return ret;
29865         
29866     }
29867 });
29868
29869
29870 Roo.form.Action.Load = function(form, options){
29871     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29872     this.reader = this.form.reader;
29873 };
29874
29875 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29876     type : 'load',
29877
29878     run : function(){
29879         
29880         Roo.Ajax.request(Roo.apply(
29881                 this.createCallback(), {
29882                     method:this.getMethod(),
29883                     url:this.getUrl(false),
29884                     params:this.getParams()
29885         }));
29886     },
29887
29888     success : function(response){
29889         
29890         var result = this.processResponse(response);
29891         if(result === true || !result.success || !result.data){
29892             this.failureType = Roo.form.Action.LOAD_FAILURE;
29893             this.form.afterAction(this, false);
29894             return;
29895         }
29896         this.form.clearInvalid();
29897         this.form.setValues(result.data);
29898         this.form.afterAction(this, true);
29899     },
29900
29901     handleResponse : function(response){
29902         if(this.form.reader){
29903             var rs = this.form.reader.read(response);
29904             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29905             return {
29906                 success : rs.success,
29907                 data : data
29908             };
29909         }
29910         return Roo.decode(response.responseText);
29911     }
29912 });
29913
29914 Roo.form.Action.ACTION_TYPES = {
29915     'load' : Roo.form.Action.Load,
29916     'submit' : Roo.form.Action.Submit
29917 };/*
29918  * Based on:
29919  * Ext JS Library 1.1.1
29920  * Copyright(c) 2006-2007, Ext JS, LLC.
29921  *
29922  * Originally Released Under LGPL - original licence link has changed is not relivant.
29923  *
29924  * Fork - LGPL
29925  * <script type="text/javascript">
29926  */
29927  
29928 /**
29929  * @class Roo.form.Layout
29930  * @extends Roo.Component
29931  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29932  * @constructor
29933  * @param {Object} config Configuration options
29934  */
29935 Roo.form.Layout = function(config){
29936     var xitems = [];
29937     if (config.items) {
29938         xitems = config.items;
29939         delete config.items;
29940     }
29941     Roo.form.Layout.superclass.constructor.call(this, config);
29942     this.stack = [];
29943     Roo.each(xitems, this.addxtype, this);
29944      
29945 };
29946
29947 Roo.extend(Roo.form.Layout, Roo.Component, {
29948     /**
29949      * @cfg {String/Object} autoCreate
29950      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29951      */
29952     /**
29953      * @cfg {String/Object/Function} style
29954      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29955      * a function which returns such a specification.
29956      */
29957     /**
29958      * @cfg {String} labelAlign
29959      * Valid values are "left," "top" and "right" (defaults to "left")
29960      */
29961     /**
29962      * @cfg {Number} labelWidth
29963      * Fixed width in pixels of all field labels (defaults to undefined)
29964      */
29965     /**
29966      * @cfg {Boolean} clear
29967      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29968      */
29969     clear : true,
29970     /**
29971      * @cfg {String} labelSeparator
29972      * The separator to use after field labels (defaults to ':')
29973      */
29974     labelSeparator : ':',
29975     /**
29976      * @cfg {Boolean} hideLabels
29977      * True to suppress the display of field labels in this layout (defaults to false)
29978      */
29979     hideLabels : false,
29980
29981     // private
29982     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29983     
29984     isLayout : true,
29985     
29986     // private
29987     onRender : function(ct, position){
29988         if(this.el){ // from markup
29989             this.el = Roo.get(this.el);
29990         }else {  // generate
29991             var cfg = this.getAutoCreate();
29992             this.el = ct.createChild(cfg, position);
29993         }
29994         if(this.style){
29995             this.el.applyStyles(this.style);
29996         }
29997         if(this.labelAlign){
29998             this.el.addClass('x-form-label-'+this.labelAlign);
29999         }
30000         if(this.hideLabels){
30001             this.labelStyle = "display:none";
30002             this.elementStyle = "padding-left:0;";
30003         }else{
30004             if(typeof this.labelWidth == 'number'){
30005                 this.labelStyle = "width:"+this.labelWidth+"px;";
30006                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30007             }
30008             if(this.labelAlign == 'top'){
30009                 this.labelStyle = "width:auto;";
30010                 this.elementStyle = "padding-left:0;";
30011             }
30012         }
30013         var stack = this.stack;
30014         var slen = stack.length;
30015         if(slen > 0){
30016             if(!this.fieldTpl){
30017                 var t = new Roo.Template(
30018                     '<div class="x-form-item {5}">',
30019                         '<label for="{0}" style="{2}">{1}{4}</label>',
30020                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30021                         '</div>',
30022                     '</div><div class="x-form-clear-left"></div>'
30023                 );
30024                 t.disableFormats = true;
30025                 t.compile();
30026                 Roo.form.Layout.prototype.fieldTpl = t;
30027             }
30028             for(var i = 0; i < slen; i++) {
30029                 if(stack[i].isFormField){
30030                     this.renderField(stack[i]);
30031                 }else{
30032                     this.renderComponent(stack[i]);
30033                 }
30034             }
30035         }
30036         if(this.clear){
30037             this.el.createChild({cls:'x-form-clear'});
30038         }
30039     },
30040
30041     // private
30042     renderField : function(f){
30043         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30044                f.id, //0
30045                f.fieldLabel, //1
30046                f.labelStyle||this.labelStyle||'', //2
30047                this.elementStyle||'', //3
30048                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30049                f.itemCls||this.itemCls||''  //5
30050        ], true).getPrevSibling());
30051     },
30052
30053     // private
30054     renderComponent : function(c){
30055         c.render(c.isLayout ? this.el : this.el.createChild());    
30056     },
30057     /**
30058      * Adds a object form elements (using the xtype property as the factory method.)
30059      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30060      * @param {Object} config 
30061      */
30062     addxtype : function(o)
30063     {
30064         // create the lement.
30065         o.form = this.form;
30066         var fe = Roo.factory(o, Roo.form);
30067         this.form.allItems.push(fe);
30068         this.stack.push(fe);
30069         
30070         if (fe.isFormField) {
30071             this.form.items.add(fe);
30072         }
30073          
30074         return fe;
30075     }
30076 });
30077
30078 /**
30079  * @class Roo.form.Column
30080  * @extends Roo.form.Layout
30081  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30082  * @constructor
30083  * @param {Object} config Configuration options
30084  */
30085 Roo.form.Column = function(config){
30086     Roo.form.Column.superclass.constructor.call(this, config);
30087 };
30088
30089 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30090     /**
30091      * @cfg {Number/String} width
30092      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30093      */
30094     /**
30095      * @cfg {String/Object} autoCreate
30096      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30097      */
30098
30099     // private
30100     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30101
30102     // private
30103     onRender : function(ct, position){
30104         Roo.form.Column.superclass.onRender.call(this, ct, position);
30105         if(this.width){
30106             this.el.setWidth(this.width);
30107         }
30108     }
30109 });
30110
30111
30112 /**
30113  * @class Roo.form.Row
30114  * @extends Roo.form.Layout
30115  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30116  * @constructor
30117  * @param {Object} config Configuration options
30118  */
30119
30120  
30121 Roo.form.Row = function(config){
30122     Roo.form.Row.superclass.constructor.call(this, config);
30123 };
30124  
30125 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30126       /**
30127      * @cfg {Number/String} width
30128      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30129      */
30130     /**
30131      * @cfg {Number/String} height
30132      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30133      */
30134     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30135     
30136     padWidth : 20,
30137     // private
30138     onRender : function(ct, position){
30139         //console.log('row render');
30140         if(!this.rowTpl){
30141             var t = new Roo.Template(
30142                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30143                     '<label for="{0}" style="{2}">{1}{4}</label>',
30144                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30145                     '</div>',
30146                 '</div>'
30147             );
30148             t.disableFormats = true;
30149             t.compile();
30150             Roo.form.Layout.prototype.rowTpl = t;
30151         }
30152         this.fieldTpl = this.rowTpl;
30153         
30154         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30155         var labelWidth = 100;
30156         
30157         if ((this.labelAlign != 'top')) {
30158             if (typeof this.labelWidth == 'number') {
30159                 labelWidth = this.labelWidth
30160             }
30161             this.padWidth =  20 + labelWidth;
30162             
30163         }
30164         
30165         Roo.form.Column.superclass.onRender.call(this, ct, position);
30166         if(this.width){
30167             this.el.setWidth(this.width);
30168         }
30169         if(this.height){
30170             this.el.setHeight(this.height);
30171         }
30172     },
30173     
30174     // private
30175     renderField : function(f){
30176         f.fieldEl = this.fieldTpl.append(this.el, [
30177                f.id, f.fieldLabel,
30178                f.labelStyle||this.labelStyle||'',
30179                this.elementStyle||'',
30180                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30181                f.itemCls||this.itemCls||'',
30182                f.width ? f.width + this.padWidth : 160 + this.padWidth
30183        ],true);
30184     }
30185 });
30186  
30187
30188 /**
30189  * @class Roo.form.FieldSet
30190  * @extends Roo.form.Layout
30191  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30192  * @constructor
30193  * @param {Object} config Configuration options
30194  */
30195 Roo.form.FieldSet = function(config){
30196     Roo.form.FieldSet.superclass.constructor.call(this, config);
30197 };
30198
30199 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30200     /**
30201      * @cfg {String} legend
30202      * The text to display as the legend for the FieldSet (defaults to '')
30203      */
30204     /**
30205      * @cfg {String/Object} autoCreate
30206      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30207      */
30208
30209     // private
30210     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30211
30212     // private
30213     onRender : function(ct, position){
30214         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30215         if(this.legend){
30216             this.setLegend(this.legend);
30217         }
30218     },
30219
30220     // private
30221     setLegend : function(text){
30222         if(this.rendered){
30223             this.el.child('legend').update(text);
30224         }
30225     }
30226 });/*
30227  * Based on:
30228  * Ext JS Library 1.1.1
30229  * Copyright(c) 2006-2007, Ext JS, LLC.
30230  *
30231  * Originally Released Under LGPL - original licence link has changed is not relivant.
30232  *
30233  * Fork - LGPL
30234  * <script type="text/javascript">
30235  */
30236 /**
30237  * @class Roo.form.VTypes
30238  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30239  * @singleton
30240  */
30241 Roo.form.VTypes = function(){
30242     // closure these in so they are only created once.
30243     var alpha = /^[a-zA-Z_]+$/;
30244     var alphanum = /^[a-zA-Z0-9_]+$/;
30245     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
30246     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30247
30248     // All these messages and functions are configurable
30249     return {
30250         /**
30251          * The function used to validate email addresses
30252          * @param {String} value The email address
30253          */
30254         'email' : function(v){
30255             return email.test(v);
30256         },
30257         /**
30258          * The error text to display when the email validation function returns false
30259          * @type String
30260          */
30261         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30262         /**
30263          * The keystroke filter mask to be applied on email input
30264          * @type RegExp
30265          */
30266         'emailMask' : /[a-z0-9_\.\-@]/i,
30267
30268         /**
30269          * The function used to validate URLs
30270          * @param {String} value The URL
30271          */
30272         'url' : function(v){
30273             return url.test(v);
30274         },
30275         /**
30276          * The error text to display when the url validation function returns false
30277          * @type String
30278          */
30279         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30280         
30281         /**
30282          * The function used to validate alpha values
30283          * @param {String} value The value
30284          */
30285         'alpha' : function(v){
30286             return alpha.test(v);
30287         },
30288         /**
30289          * The error text to display when the alpha validation function returns false
30290          * @type String
30291          */
30292         'alphaText' : 'This field should only contain letters and _',
30293         /**
30294          * The keystroke filter mask to be applied on alpha input
30295          * @type RegExp
30296          */
30297         'alphaMask' : /[a-z_]/i,
30298
30299         /**
30300          * The function used to validate alphanumeric values
30301          * @param {String} value The value
30302          */
30303         'alphanum' : function(v){
30304             return alphanum.test(v);
30305         },
30306         /**
30307          * The error text to display when the alphanumeric validation function returns false
30308          * @type String
30309          */
30310         'alphanumText' : 'This field should only contain letters, numbers and _',
30311         /**
30312          * The keystroke filter mask to be applied on alphanumeric input
30313          * @type RegExp
30314          */
30315         'alphanumMask' : /[a-z0-9_]/i
30316     };
30317 }();//<script type="text/javascript">
30318
30319 /**
30320  * @class Roo.form.FCKeditor
30321  * @extends Roo.form.TextArea
30322  * Wrapper around the FCKEditor http://www.fckeditor.net
30323  * @constructor
30324  * Creates a new FCKeditor
30325  * @param {Object} config Configuration options
30326  */
30327 Roo.form.FCKeditor = function(config){
30328     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30329     this.addEvents({
30330          /**
30331          * @event editorinit
30332          * Fired when the editor is initialized - you can add extra handlers here..
30333          * @param {FCKeditor} this
30334          * @param {Object} the FCK object.
30335          */
30336         editorinit : true
30337     });
30338     
30339     
30340 };
30341 Roo.form.FCKeditor.editors = { };
30342 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30343 {
30344     //defaultAutoCreate : {
30345     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30346     //},
30347     // private
30348     /**
30349      * @cfg {Object} fck options - see fck manual for details.
30350      */
30351     fckconfig : false,
30352     
30353     /**
30354      * @cfg {Object} fck toolbar set (Basic or Default)
30355      */
30356     toolbarSet : 'Basic',
30357     /**
30358      * @cfg {Object} fck BasePath
30359      */ 
30360     basePath : '/fckeditor/',
30361     
30362     
30363     frame : false,
30364     
30365     value : '',
30366     
30367    
30368     onRender : function(ct, position)
30369     {
30370         if(!this.el){
30371             this.defaultAutoCreate = {
30372                 tag: "textarea",
30373                 style:"width:300px;height:60px;",
30374                 autocomplete: "new-password"
30375             };
30376         }
30377         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30378         /*
30379         if(this.grow){
30380             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30381             if(this.preventScrollbars){
30382                 this.el.setStyle("overflow", "hidden");
30383             }
30384             this.el.setHeight(this.growMin);
30385         }
30386         */
30387         //console.log('onrender' + this.getId() );
30388         Roo.form.FCKeditor.editors[this.getId()] = this;
30389          
30390
30391         this.replaceTextarea() ;
30392         
30393     },
30394     
30395     getEditor : function() {
30396         return this.fckEditor;
30397     },
30398     /**
30399      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30400      * @param {Mixed} value The value to set
30401      */
30402     
30403     
30404     setValue : function(value)
30405     {
30406         //console.log('setValue: ' + value);
30407         
30408         if(typeof(value) == 'undefined') { // not sure why this is happending...
30409             return;
30410         }
30411         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30412         
30413         //if(!this.el || !this.getEditor()) {
30414         //    this.value = value;
30415             //this.setValue.defer(100,this,[value]);    
30416         //    return;
30417         //} 
30418         
30419         if(!this.getEditor()) {
30420             return;
30421         }
30422         
30423         this.getEditor().SetData(value);
30424         
30425         //
30426
30427     },
30428
30429     /**
30430      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30431      * @return {Mixed} value The field value
30432      */
30433     getValue : function()
30434     {
30435         
30436         if (this.frame && this.frame.dom.style.display == 'none') {
30437             return Roo.form.FCKeditor.superclass.getValue.call(this);
30438         }
30439         
30440         if(!this.el || !this.getEditor()) {
30441            
30442            // this.getValue.defer(100,this); 
30443             return this.value;
30444         }
30445        
30446         
30447         var value=this.getEditor().GetData();
30448         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30449         return Roo.form.FCKeditor.superclass.getValue.call(this);
30450         
30451
30452     },
30453
30454     /**
30455      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30456      * @return {Mixed} value The field value
30457      */
30458     getRawValue : function()
30459     {
30460         if (this.frame && this.frame.dom.style.display == 'none') {
30461             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30462         }
30463         
30464         if(!this.el || !this.getEditor()) {
30465             //this.getRawValue.defer(100,this); 
30466             return this.value;
30467             return;
30468         }
30469         
30470         
30471         
30472         var value=this.getEditor().GetData();
30473         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30474         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30475          
30476     },
30477     
30478     setSize : function(w,h) {
30479         
30480         
30481         
30482         //if (this.frame && this.frame.dom.style.display == 'none') {
30483         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30484         //    return;
30485         //}
30486         //if(!this.el || !this.getEditor()) {
30487         //    this.setSize.defer(100,this, [w,h]); 
30488         //    return;
30489         //}
30490         
30491         
30492         
30493         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30494         
30495         this.frame.dom.setAttribute('width', w);
30496         this.frame.dom.setAttribute('height', h);
30497         this.frame.setSize(w,h);
30498         
30499     },
30500     
30501     toggleSourceEdit : function(value) {
30502         
30503       
30504          
30505         this.el.dom.style.display = value ? '' : 'none';
30506         this.frame.dom.style.display = value ?  'none' : '';
30507         
30508     },
30509     
30510     
30511     focus: function(tag)
30512     {
30513         if (this.frame.dom.style.display == 'none') {
30514             return Roo.form.FCKeditor.superclass.focus.call(this);
30515         }
30516         if(!this.el || !this.getEditor()) {
30517             this.focus.defer(100,this, [tag]); 
30518             return;
30519         }
30520         
30521         
30522         
30523         
30524         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30525         this.getEditor().Focus();
30526         if (tgs.length) {
30527             if (!this.getEditor().Selection.GetSelection()) {
30528                 this.focus.defer(100,this, [tag]); 
30529                 return;
30530             }
30531             
30532             
30533             var r = this.getEditor().EditorDocument.createRange();
30534             r.setStart(tgs[0],0);
30535             r.setEnd(tgs[0],0);
30536             this.getEditor().Selection.GetSelection().removeAllRanges();
30537             this.getEditor().Selection.GetSelection().addRange(r);
30538             this.getEditor().Focus();
30539         }
30540         
30541     },
30542     
30543     
30544     
30545     replaceTextarea : function()
30546     {
30547         if ( document.getElementById( this.getId() + '___Frame' ) )
30548             return ;
30549         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30550         //{
30551             // We must check the elements firstly using the Id and then the name.
30552         var oTextarea = document.getElementById( this.getId() );
30553         
30554         var colElementsByName = document.getElementsByName( this.getId() ) ;
30555          
30556         oTextarea.style.display = 'none' ;
30557
30558         if ( oTextarea.tabIndex ) {            
30559             this.TabIndex = oTextarea.tabIndex ;
30560         }
30561         
30562         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30563         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30564         this.frame = Roo.get(this.getId() + '___Frame')
30565     },
30566     
30567     _getConfigHtml : function()
30568     {
30569         var sConfig = '' ;
30570
30571         for ( var o in this.fckconfig ) {
30572             sConfig += sConfig.length > 0  ? '&amp;' : '';
30573             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30574         }
30575
30576         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30577     },
30578     
30579     
30580     _getIFrameHtml : function()
30581     {
30582         var sFile = 'fckeditor.html' ;
30583         /* no idea what this is about..
30584         try
30585         {
30586             if ( (/fcksource=true/i).test( window.top.location.search ) )
30587                 sFile = 'fckeditor.original.html' ;
30588         }
30589         catch (e) { 
30590         */
30591
30592         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30593         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30594         
30595         
30596         var html = '<iframe id="' + this.getId() +
30597             '___Frame" src="' + sLink +
30598             '" width="' + this.width +
30599             '" height="' + this.height + '"' +
30600             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30601             ' frameborder="0" scrolling="no"></iframe>' ;
30602
30603         return html ;
30604     },
30605     
30606     _insertHtmlBefore : function( html, element )
30607     {
30608         if ( element.insertAdjacentHTML )       {
30609             // IE
30610             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30611         } else { // Gecko
30612             var oRange = document.createRange() ;
30613             oRange.setStartBefore( element ) ;
30614             var oFragment = oRange.createContextualFragment( html );
30615             element.parentNode.insertBefore( oFragment, element ) ;
30616         }
30617     }
30618     
30619     
30620   
30621     
30622     
30623     
30624     
30625
30626 });
30627
30628 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30629
30630 function FCKeditor_OnComplete(editorInstance){
30631     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30632     f.fckEditor = editorInstance;
30633     //console.log("loaded");
30634     f.fireEvent('editorinit', f, editorInstance);
30635
30636   
30637
30638  
30639
30640
30641
30642
30643
30644
30645
30646
30647
30648
30649
30650
30651
30652
30653
30654 //<script type="text/javascript">
30655 /**
30656  * @class Roo.form.GridField
30657  * @extends Roo.form.Field
30658  * Embed a grid (or editable grid into a form)
30659  * STATUS ALPHA
30660  * 
30661  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30662  * it needs 
30663  * xgrid.store = Roo.data.Store
30664  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30665  * xgrid.store.reader = Roo.data.JsonReader 
30666  * 
30667  * 
30668  * @constructor
30669  * Creates a new GridField
30670  * @param {Object} config Configuration options
30671  */
30672 Roo.form.GridField = function(config){
30673     Roo.form.GridField.superclass.constructor.call(this, config);
30674      
30675 };
30676
30677 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30678     /**
30679      * @cfg {Number} width  - used to restrict width of grid..
30680      */
30681     width : 100,
30682     /**
30683      * @cfg {Number} height - used to restrict height of grid..
30684      */
30685     height : 50,
30686      /**
30687      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30688          * 
30689          *}
30690      */
30691     xgrid : false, 
30692     /**
30693      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30694      * {tag: "input", type: "checkbox", autocomplete: "off"})
30695      */
30696    // defaultAutoCreate : { tag: 'div' },
30697     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30698     /**
30699      * @cfg {String} addTitle Text to include for adding a title.
30700      */
30701     addTitle : false,
30702     //
30703     onResize : function(){
30704         Roo.form.Field.superclass.onResize.apply(this, arguments);
30705     },
30706
30707     initEvents : function(){
30708         // Roo.form.Checkbox.superclass.initEvents.call(this);
30709         // has no events...
30710        
30711     },
30712
30713
30714     getResizeEl : function(){
30715         return this.wrap;
30716     },
30717
30718     getPositionEl : function(){
30719         return this.wrap;
30720     },
30721
30722     // private
30723     onRender : function(ct, position){
30724         
30725         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30726         var style = this.style;
30727         delete this.style;
30728         
30729         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30730         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30731         this.viewEl = this.wrap.createChild({ tag: 'div' });
30732         if (style) {
30733             this.viewEl.applyStyles(style);
30734         }
30735         if (this.width) {
30736             this.viewEl.setWidth(this.width);
30737         }
30738         if (this.height) {
30739             this.viewEl.setHeight(this.height);
30740         }
30741         //if(this.inputValue !== undefined){
30742         //this.setValue(this.value);
30743         
30744         
30745         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30746         
30747         
30748         this.grid.render();
30749         this.grid.getDataSource().on('remove', this.refreshValue, this);
30750         this.grid.getDataSource().on('update', this.refreshValue, this);
30751         this.grid.on('afteredit', this.refreshValue, this);
30752  
30753     },
30754      
30755     
30756     /**
30757      * Sets the value of the item. 
30758      * @param {String} either an object  or a string..
30759      */
30760     setValue : function(v){
30761         //this.value = v;
30762         v = v || []; // empty set..
30763         // this does not seem smart - it really only affects memoryproxy grids..
30764         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30765             var ds = this.grid.getDataSource();
30766             // assumes a json reader..
30767             var data = {}
30768             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30769             ds.loadData( data);
30770         }
30771         // clear selection so it does not get stale.
30772         if (this.grid.sm) { 
30773             this.grid.sm.clearSelections();
30774         }
30775         
30776         Roo.form.GridField.superclass.setValue.call(this, v);
30777         this.refreshValue();
30778         // should load data in the grid really....
30779     },
30780     
30781     // private
30782     refreshValue: function() {
30783          var val = [];
30784         this.grid.getDataSource().each(function(r) {
30785             val.push(r.data);
30786         });
30787         this.el.dom.value = Roo.encode(val);
30788     }
30789     
30790      
30791     
30792     
30793 });/*
30794  * Based on:
30795  * Ext JS Library 1.1.1
30796  * Copyright(c) 2006-2007, Ext JS, LLC.
30797  *
30798  * Originally Released Under LGPL - original licence link has changed is not relivant.
30799  *
30800  * Fork - LGPL
30801  * <script type="text/javascript">
30802  */
30803 /**
30804  * @class Roo.form.DisplayField
30805  * @extends Roo.form.Field
30806  * A generic Field to display non-editable data.
30807  * @constructor
30808  * Creates a new Display Field item.
30809  * @param {Object} config Configuration options
30810  */
30811 Roo.form.DisplayField = function(config){
30812     Roo.form.DisplayField.superclass.constructor.call(this, config);
30813     
30814 };
30815
30816 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30817     inputType:      'hidden',
30818     allowBlank:     true,
30819     readOnly:         true,
30820     
30821  
30822     /**
30823      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30824      */
30825     focusClass : undefined,
30826     /**
30827      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30828      */
30829     fieldClass: 'x-form-field',
30830     
30831      /**
30832      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30833      */
30834     valueRenderer: undefined,
30835     
30836     width: 100,
30837     /**
30838      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30839      * {tag: "input", type: "checkbox", autocomplete: "off"})
30840      */
30841      
30842  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30843
30844     onResize : function(){
30845         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30846         
30847     },
30848
30849     initEvents : function(){
30850         // Roo.form.Checkbox.superclass.initEvents.call(this);
30851         // has no events...
30852        
30853     },
30854
30855
30856     getResizeEl : function(){
30857         return this.wrap;
30858     },
30859
30860     getPositionEl : function(){
30861         return this.wrap;
30862     },
30863
30864     // private
30865     onRender : function(ct, position){
30866         
30867         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30868         //if(this.inputValue !== undefined){
30869         this.wrap = this.el.wrap();
30870         
30871         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30872         
30873         if (this.bodyStyle) {
30874             this.viewEl.applyStyles(this.bodyStyle);
30875         }
30876         //this.viewEl.setStyle('padding', '2px');
30877         
30878         this.setValue(this.value);
30879         
30880     },
30881 /*
30882     // private
30883     initValue : Roo.emptyFn,
30884
30885   */
30886
30887         // private
30888     onClick : function(){
30889         
30890     },
30891
30892     /**
30893      * Sets the checked state of the checkbox.
30894      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30895      */
30896     setValue : function(v){
30897         this.value = v;
30898         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30899         // this might be called before we have a dom element..
30900         if (!this.viewEl) {
30901             return;
30902         }
30903         this.viewEl.dom.innerHTML = html;
30904         Roo.form.DisplayField.superclass.setValue.call(this, v);
30905
30906     }
30907 });/*
30908  * 
30909  * Licence- LGPL
30910  * 
30911  */
30912
30913 /**
30914  * @class Roo.form.DayPicker
30915  * @extends Roo.form.Field
30916  * A Day picker show [M] [T] [W] ....
30917  * @constructor
30918  * Creates a new Day Picker
30919  * @param {Object} config Configuration options
30920  */
30921 Roo.form.DayPicker= function(config){
30922     Roo.form.DayPicker.superclass.constructor.call(this, config);
30923      
30924 };
30925
30926 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30927     /**
30928      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30929      */
30930     focusClass : undefined,
30931     /**
30932      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30933      */
30934     fieldClass: "x-form-field",
30935    
30936     /**
30937      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30938      * {tag: "input", type: "checkbox", autocomplete: "off"})
30939      */
30940     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
30941     
30942    
30943     actionMode : 'viewEl', 
30944     //
30945     // private
30946  
30947     inputType : 'hidden',
30948     
30949      
30950     inputElement: false, // real input element?
30951     basedOn: false, // ????
30952     
30953     isFormField: true, // not sure where this is needed!!!!
30954
30955     onResize : function(){
30956         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30957         if(!this.boxLabel){
30958             this.el.alignTo(this.wrap, 'c-c');
30959         }
30960     },
30961
30962     initEvents : function(){
30963         Roo.form.Checkbox.superclass.initEvents.call(this);
30964         this.el.on("click", this.onClick,  this);
30965         this.el.on("change", this.onClick,  this);
30966     },
30967
30968
30969     getResizeEl : function(){
30970         return this.wrap;
30971     },
30972
30973     getPositionEl : function(){
30974         return this.wrap;
30975     },
30976
30977     
30978     // private
30979     onRender : function(ct, position){
30980         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30981        
30982         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30983         
30984         var r1 = '<table><tr>';
30985         var r2 = '<tr class="x-form-daypick-icons">';
30986         for (var i=0; i < 7; i++) {
30987             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30988             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30989         }
30990         
30991         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30992         viewEl.select('img').on('click', this.onClick, this);
30993         this.viewEl = viewEl;   
30994         
30995         
30996         // this will not work on Chrome!!!
30997         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30998         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30999         
31000         
31001           
31002
31003     },
31004
31005     // private
31006     initValue : Roo.emptyFn,
31007
31008     /**
31009      * Returns the checked state of the checkbox.
31010      * @return {Boolean} True if checked, else false
31011      */
31012     getValue : function(){
31013         return this.el.dom.value;
31014         
31015     },
31016
31017         // private
31018     onClick : function(e){ 
31019         //this.setChecked(!this.checked);
31020         Roo.get(e.target).toggleClass('x-menu-item-checked');
31021         this.refreshValue();
31022         //if(this.el.dom.checked != this.checked){
31023         //    this.setValue(this.el.dom.checked);
31024        // }
31025     },
31026     
31027     // private
31028     refreshValue : function()
31029     {
31030         var val = '';
31031         this.viewEl.select('img',true).each(function(e,i,n)  {
31032             val += e.is(".x-menu-item-checked") ? String(n) : '';
31033         });
31034         this.setValue(val, true);
31035     },
31036
31037     /**
31038      * Sets the checked state of the checkbox.
31039      * On is always based on a string comparison between inputValue and the param.
31040      * @param {Boolean/String} value - the value to set 
31041      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31042      */
31043     setValue : function(v,suppressEvent){
31044         if (!this.el.dom) {
31045             return;
31046         }
31047         var old = this.el.dom.value ;
31048         this.el.dom.value = v;
31049         if (suppressEvent) {
31050             return ;
31051         }
31052          
31053         // update display..
31054         this.viewEl.select('img',true).each(function(e,i,n)  {
31055             
31056             var on = e.is(".x-menu-item-checked");
31057             var newv = v.indexOf(String(n)) > -1;
31058             if (on != newv) {
31059                 e.toggleClass('x-menu-item-checked');
31060             }
31061             
31062         });
31063         
31064         
31065         this.fireEvent('change', this, v, old);
31066         
31067         
31068     },
31069    
31070     // handle setting of hidden value by some other method!!?!?
31071     setFromHidden: function()
31072     {
31073         if(!this.el){
31074             return;
31075         }
31076         //console.log("SET FROM HIDDEN");
31077         //alert('setFrom hidden');
31078         this.setValue(this.el.dom.value);
31079     },
31080     
31081     onDestroy : function()
31082     {
31083         if(this.viewEl){
31084             Roo.get(this.viewEl).remove();
31085         }
31086          
31087         Roo.form.DayPicker.superclass.onDestroy.call(this);
31088     }
31089
31090 });/*
31091  * RooJS Library 1.1.1
31092  * Copyright(c) 2008-2011  Alan Knowles
31093  *
31094  * License - LGPL
31095  */
31096  
31097
31098 /**
31099  * @class Roo.form.ComboCheck
31100  * @extends Roo.form.ComboBox
31101  * A combobox for multiple select items.
31102  *
31103  * FIXME - could do with a reset button..
31104  * 
31105  * @constructor
31106  * Create a new ComboCheck
31107  * @param {Object} config Configuration options
31108  */
31109 Roo.form.ComboCheck = function(config){
31110     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31111     // should verify some data...
31112     // like
31113     // hiddenName = required..
31114     // displayField = required
31115     // valudField == required
31116     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31117     var _t = this;
31118     Roo.each(req, function(e) {
31119         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31120             throw "Roo.form.ComboCheck : missing value for: " + e;
31121         }
31122     });
31123     
31124     
31125 };
31126
31127 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31128      
31129      
31130     editable : false,
31131      
31132     selectedClass: 'x-menu-item-checked', 
31133     
31134     // private
31135     onRender : function(ct, position){
31136         var _t = this;
31137         
31138         
31139         
31140         if(!this.tpl){
31141             var cls = 'x-combo-list';
31142
31143             
31144             this.tpl =  new Roo.Template({
31145                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31146                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31147                    '<span>{' + this.displayField + '}</span>' +
31148                     '</div>' 
31149                 
31150             });
31151         }
31152  
31153         
31154         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31155         this.view.singleSelect = false;
31156         this.view.multiSelect = true;
31157         this.view.toggleSelect = true;
31158         this.pageTb.add(new Roo.Toolbar.Fill(), {
31159             
31160             text: 'Done',
31161             handler: function()
31162             {
31163                 _t.collapse();
31164             }
31165         });
31166     },
31167     
31168     onViewOver : function(e, t){
31169         // do nothing...
31170         return;
31171         
31172     },
31173     
31174     onViewClick : function(doFocus,index){
31175         return;
31176         
31177     },
31178     select: function () {
31179         //Roo.log("SELECT CALLED");
31180     },
31181      
31182     selectByValue : function(xv, scrollIntoView){
31183         var ar = this.getValueArray();
31184         var sels = [];
31185         
31186         Roo.each(ar, function(v) {
31187             if(v === undefined || v === null){
31188                 return;
31189             }
31190             var r = this.findRecord(this.valueField, v);
31191             if(r){
31192                 sels.push(this.store.indexOf(r))
31193                 
31194             }
31195         },this);
31196         this.view.select(sels);
31197         return false;
31198     },
31199     
31200     
31201     
31202     onSelect : function(record, index){
31203        // Roo.log("onselect Called");
31204        // this is only called by the clear button now..
31205         this.view.clearSelections();
31206         this.setValue('[]');
31207         if (this.value != this.valueBefore) {
31208             this.fireEvent('change', this, this.value, this.valueBefore);
31209             this.valueBefore = this.value;
31210         }
31211     },
31212     getValueArray : function()
31213     {
31214         var ar = [] ;
31215         
31216         try {
31217             //Roo.log(this.value);
31218             if (typeof(this.value) == 'undefined') {
31219                 return [];
31220             }
31221             var ar = Roo.decode(this.value);
31222             return  ar instanceof Array ? ar : []; //?? valid?
31223             
31224         } catch(e) {
31225             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31226             return [];
31227         }
31228          
31229     },
31230     expand : function ()
31231     {
31232         
31233         Roo.form.ComboCheck.superclass.expand.call(this);
31234         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31235         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31236         
31237
31238     },
31239     
31240     collapse : function(){
31241         Roo.form.ComboCheck.superclass.collapse.call(this);
31242         var sl = this.view.getSelectedIndexes();
31243         var st = this.store;
31244         var nv = [];
31245         var tv = [];
31246         var r;
31247         Roo.each(sl, function(i) {
31248             r = st.getAt(i);
31249             nv.push(r.get(this.valueField));
31250         },this);
31251         this.setValue(Roo.encode(nv));
31252         if (this.value != this.valueBefore) {
31253
31254             this.fireEvent('change', this, this.value, this.valueBefore);
31255             this.valueBefore = this.value;
31256         }
31257         
31258     },
31259     
31260     setValue : function(v){
31261         // Roo.log(v);
31262         this.value = v;
31263         
31264         var vals = this.getValueArray();
31265         var tv = [];
31266         Roo.each(vals, function(k) {
31267             var r = this.findRecord(this.valueField, k);
31268             if(r){
31269                 tv.push(r.data[this.displayField]);
31270             }else if(this.valueNotFoundText !== undefined){
31271                 tv.push( this.valueNotFoundText );
31272             }
31273         },this);
31274        // Roo.log(tv);
31275         
31276         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31277         this.hiddenField.value = v;
31278         this.value = v;
31279     }
31280     
31281 });/*
31282  * Based on:
31283  * Ext JS Library 1.1.1
31284  * Copyright(c) 2006-2007, Ext JS, LLC.
31285  *
31286  * Originally Released Under LGPL - original licence link has changed is not relivant.
31287  *
31288  * Fork - LGPL
31289  * <script type="text/javascript">
31290  */
31291  
31292 /**
31293  * @class Roo.form.Signature
31294  * @extends Roo.form.Field
31295  * Signature field.  
31296  * @constructor
31297  * 
31298  * @param {Object} config Configuration options
31299  */
31300
31301 Roo.form.Signature = function(config){
31302     Roo.form.Signature.superclass.constructor.call(this, config);
31303     
31304     this.addEvents({// not in used??
31305          /**
31306          * @event confirm
31307          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31308              * @param {Roo.form.Signature} combo This combo box
31309              */
31310         'confirm' : true,
31311         /**
31312          * @event reset
31313          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31314              * @param {Roo.form.ComboBox} combo This combo box
31315              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31316              */
31317         'reset' : true
31318     });
31319 };
31320
31321 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31322     /**
31323      * @cfg {Object} labels Label to use when rendering a form.
31324      * defaults to 
31325      * labels : { 
31326      *      clear : "Clear",
31327      *      confirm : "Confirm"
31328      *  }
31329      */
31330     labels : { 
31331         clear : "Clear",
31332         confirm : "Confirm"
31333     },
31334     /**
31335      * @cfg {Number} width The signature panel width (defaults to 300)
31336      */
31337     width: 300,
31338     /**
31339      * @cfg {Number} height The signature panel height (defaults to 100)
31340      */
31341     height : 100,
31342     /**
31343      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31344      */
31345     allowBlank : false,
31346     
31347     //private
31348     // {Object} signPanel The signature SVG panel element (defaults to {})
31349     signPanel : {},
31350     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31351     isMouseDown : false,
31352     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31353     isConfirmed : false,
31354     // {String} signatureTmp SVG mapping string (defaults to empty string)
31355     signatureTmp : '',
31356     
31357     
31358     defaultAutoCreate : { // modified by initCompnoent..
31359         tag: "input",
31360         type:"hidden"
31361     },
31362
31363     // private
31364     onRender : function(ct, position){
31365         
31366         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31367         
31368         this.wrap = this.el.wrap({
31369             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31370         });
31371         
31372         this.createToolbar(this);
31373         this.signPanel = this.wrap.createChild({
31374                 tag: 'div',
31375                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31376             }, this.el
31377         );
31378             
31379         this.svgID = Roo.id();
31380         this.svgEl = this.signPanel.createChild({
31381               xmlns : 'http://www.w3.org/2000/svg',
31382               tag : 'svg',
31383               id : this.svgID + "-svg",
31384               width: this.width,
31385               height: this.height,
31386               viewBox: '0 0 '+this.width+' '+this.height,
31387               cn : [
31388                 {
31389                     tag: "rect",
31390                     id: this.svgID + "-svg-r",
31391                     width: this.width,
31392                     height: this.height,
31393                     fill: "#ffa"
31394                 },
31395                 {
31396                     tag: "line",
31397                     id: this.svgID + "-svg-l",
31398                     x1: "0", // start
31399                     y1: (this.height*0.8), // start set the line in 80% of height
31400                     x2: this.width, // end
31401                     y2: (this.height*0.8), // end set the line in 80% of height
31402                     'stroke': "#666",
31403                     'stroke-width': "1",
31404                     'stroke-dasharray': "3",
31405                     'shape-rendering': "crispEdges",
31406                     'pointer-events': "none"
31407                 },
31408                 {
31409                     tag: "path",
31410                     id: this.svgID + "-svg-p",
31411                     'stroke': "navy",
31412                     'stroke-width': "3",
31413                     'fill': "none",
31414                     'pointer-events': 'none'
31415                 }
31416               ]
31417         });
31418         this.createSVG();
31419         this.svgBox = this.svgEl.dom.getScreenCTM();
31420     },
31421     createSVG : function(){ 
31422         var svg = this.signPanel;
31423         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31424         var t = this;
31425
31426         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31427         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31428         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31429         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31430         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31431         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31432         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31433         
31434     },
31435     isTouchEvent : function(e){
31436         return e.type.match(/^touch/);
31437     },
31438     getCoords : function (e) {
31439         var pt    = this.svgEl.dom.createSVGPoint();
31440         pt.x = e.clientX; 
31441         pt.y = e.clientY;
31442         if (this.isTouchEvent(e)) {
31443             pt.x =  e.targetTouches[0].clientX 
31444             pt.y = e.targetTouches[0].clientY;
31445         }
31446         var a = this.svgEl.dom.getScreenCTM();
31447         var b = a.inverse();
31448         var mx = pt.matrixTransform(b);
31449         return mx.x + ',' + mx.y;
31450     },
31451     //mouse event headler 
31452     down : function (e) {
31453         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31454         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31455         
31456         this.isMouseDown = true;
31457         
31458         e.preventDefault();
31459     },
31460     move : function (e) {
31461         if (this.isMouseDown) {
31462             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31463             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31464         }
31465         
31466         e.preventDefault();
31467     },
31468     up : function (e) {
31469         this.isMouseDown = false;
31470         var sp = this.signatureTmp.split(' ');
31471         
31472         if(sp.length > 1){
31473             if(!sp[sp.length-2].match(/^L/)){
31474                 sp.pop();
31475                 sp.pop();
31476                 sp.push("");
31477                 this.signatureTmp = sp.join(" ");
31478             }
31479         }
31480         if(this.getValue() != this.signatureTmp){
31481             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31482             this.isConfirmed = false;
31483         }
31484         e.preventDefault();
31485     },
31486     
31487     /**
31488      * Protected method that will not generally be called directly. It
31489      * is called when the editor creates its toolbar. Override this method if you need to
31490      * add custom toolbar buttons.
31491      * @param {HtmlEditor} editor
31492      */
31493     createToolbar : function(editor){
31494          function btn(id, toggle, handler){
31495             var xid = fid + '-'+ id ;
31496             return {
31497                 id : xid,
31498                 cmd : id,
31499                 cls : 'x-btn-icon x-edit-'+id,
31500                 enableToggle:toggle !== false,
31501                 scope: editor, // was editor...
31502                 handler:handler||editor.relayBtnCmd,
31503                 clickEvent:'mousedown',
31504                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31505                 tabIndex:-1
31506             };
31507         }
31508         
31509         
31510         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31511         this.tb = tb;
31512         this.tb.add(
31513            {
31514                 cls : ' x-signature-btn x-signature-'+id,
31515                 scope: editor, // was editor...
31516                 handler: this.reset,
31517                 clickEvent:'mousedown',
31518                 text: this.labels.clear
31519             },
31520             {
31521                  xtype : 'Fill',
31522                  xns: Roo.Toolbar
31523             }, 
31524             {
31525                 cls : '  x-signature-btn x-signature-'+id,
31526                 scope: editor, // was editor...
31527                 handler: this.confirmHandler,
31528                 clickEvent:'mousedown',
31529                 text: this.labels.confirm
31530             }
31531         );
31532     
31533     },
31534     //public
31535     /**
31536      * when user is clicked confirm then show this image.....
31537      * 
31538      * @return {String} Image Data URI
31539      */
31540     getImageDataURI : function(){
31541         var svg = this.svgEl.dom.parentNode.innerHTML;
31542         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31543         return src; 
31544     },
31545     /**
31546      * 
31547      * @return {Boolean} this.isConfirmed
31548      */
31549     getConfirmed : function(){
31550         return this.isConfirmed;
31551     },
31552     /**
31553      * 
31554      * @return {Number} this.width
31555      */
31556     getWidth : function(){
31557         return this.width;
31558     },
31559     /**
31560      * 
31561      * @return {Number} this.height
31562      */
31563     getHeight : function(){
31564         return this.height;
31565     },
31566     // private
31567     getSignature : function(){
31568         return this.signatureTmp;
31569     },
31570     // private
31571     reset : function(){
31572         this.signatureTmp = '';
31573         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31574         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31575         this.isConfirmed = false;
31576         Roo.form.Signature.superclass.reset.call(this);
31577     },
31578     setSignature : function(s){
31579         this.signatureTmp = s;
31580         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31581         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31582         this.setValue(s);
31583         this.isConfirmed = false;
31584         Roo.form.Signature.superclass.reset.call(this);
31585     }, 
31586     test : function(){
31587 //        Roo.log(this.signPanel.dom.contentWindow.up())
31588     },
31589     //private
31590     setConfirmed : function(){
31591         
31592         
31593         
31594 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31595     },
31596     // private
31597     confirmHandler : function(){
31598         if(!this.getSignature()){
31599             return;
31600         }
31601         
31602         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31603         this.setValue(this.getSignature());
31604         this.isConfirmed = true;
31605         
31606         this.fireEvent('confirm', this);
31607     },
31608     // private
31609     // Subclasses should provide the validation implementation by overriding this
31610     validateValue : function(value){
31611         if(this.allowBlank){
31612             return true;
31613         }
31614         
31615         if(this.isConfirmed){
31616             return true;
31617         }
31618         return false;
31619     }
31620 });/*
31621  * Based on:
31622  * Ext JS Library 1.1.1
31623  * Copyright(c) 2006-2007, Ext JS, LLC.
31624  *
31625  * Originally Released Under LGPL - original licence link has changed is not relivant.
31626  *
31627  * Fork - LGPL
31628  * <script type="text/javascript">
31629  */
31630  
31631
31632 /**
31633  * @class Roo.form.ComboBox
31634  * @extends Roo.form.TriggerField
31635  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31636  * @constructor
31637  * Create a new ComboBox.
31638  * @param {Object} config Configuration options
31639  */
31640 Roo.form.Select = function(config){
31641     Roo.form.Select.superclass.constructor.call(this, config);
31642      
31643 };
31644
31645 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31646     /**
31647      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31648      */
31649     /**
31650      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31651      * rendering into an Roo.Editor, defaults to false)
31652      */
31653     /**
31654      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31655      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31656      */
31657     /**
31658      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31659      */
31660     /**
31661      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31662      * the dropdown list (defaults to undefined, with no header element)
31663      */
31664
31665      /**
31666      * @cfg {String/Roo.Template} tpl The template to use to render the output
31667      */
31668      
31669     // private
31670     defaultAutoCreate : {tag: "select"  },
31671     /**
31672      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31673      */
31674     listWidth: undefined,
31675     /**
31676      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31677      * mode = 'remote' or 'text' if mode = 'local')
31678      */
31679     displayField: undefined,
31680     /**
31681      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31682      * mode = 'remote' or 'value' if mode = 'local'). 
31683      * Note: use of a valueField requires the user make a selection
31684      * in order for a value to be mapped.
31685      */
31686     valueField: undefined,
31687     
31688     
31689     /**
31690      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31691      * field's data value (defaults to the underlying DOM element's name)
31692      */
31693     hiddenName: undefined,
31694     /**
31695      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31696      */
31697     listClass: '',
31698     /**
31699      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31700      */
31701     selectedClass: 'x-combo-selected',
31702     /**
31703      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31704      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31705      * which displays a downward arrow icon).
31706      */
31707     triggerClass : 'x-form-arrow-trigger',
31708     /**
31709      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31710      */
31711     shadow:'sides',
31712     /**
31713      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31714      * anchor positions (defaults to 'tl-bl')
31715      */
31716     listAlign: 'tl-bl?',
31717     /**
31718      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31719      */
31720     maxHeight: 300,
31721     /**
31722      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31723      * query specified by the allQuery config option (defaults to 'query')
31724      */
31725     triggerAction: 'query',
31726     /**
31727      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31728      * (defaults to 4, does not apply if editable = false)
31729      */
31730     minChars : 4,
31731     /**
31732      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31733      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31734      */
31735     typeAhead: false,
31736     /**
31737      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31738      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31739      */
31740     queryDelay: 500,
31741     /**
31742      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31743      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31744      */
31745     pageSize: 0,
31746     /**
31747      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31748      * when editable = true (defaults to false)
31749      */
31750     selectOnFocus:false,
31751     /**
31752      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31753      */
31754     queryParam: 'query',
31755     /**
31756      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31757      * when mode = 'remote' (defaults to 'Loading...')
31758      */
31759     loadingText: 'Loading...',
31760     /**
31761      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31762      */
31763     resizable: false,
31764     /**
31765      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31766      */
31767     handleHeight : 8,
31768     /**
31769      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31770      * traditional select (defaults to true)
31771      */
31772     editable: true,
31773     /**
31774      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31775      */
31776     allQuery: '',
31777     /**
31778      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31779      */
31780     mode: 'remote',
31781     /**
31782      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31783      * listWidth has a higher value)
31784      */
31785     minListWidth : 70,
31786     /**
31787      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31788      * allow the user to set arbitrary text into the field (defaults to false)
31789      */
31790     forceSelection:false,
31791     /**
31792      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31793      * if typeAhead = true (defaults to 250)
31794      */
31795     typeAheadDelay : 250,
31796     /**
31797      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31798      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31799      */
31800     valueNotFoundText : undefined,
31801     
31802     /**
31803      * @cfg {String} defaultValue The value displayed after loading the store.
31804      */
31805     defaultValue: '',
31806     
31807     /**
31808      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31809      */
31810     blockFocus : false,
31811     
31812     /**
31813      * @cfg {Boolean} disableClear Disable showing of clear button.
31814      */
31815     disableClear : false,
31816     /**
31817      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31818      */
31819     alwaysQuery : false,
31820     
31821     //private
31822     addicon : false,
31823     editicon: false,
31824     
31825     // element that contains real text value.. (when hidden is used..)
31826      
31827     // private
31828     onRender : function(ct, position){
31829         Roo.form.Field.prototype.onRender.call(this, ct, position);
31830         
31831         if(this.store){
31832             this.store.on('beforeload', this.onBeforeLoad, this);
31833             this.store.on('load', this.onLoad, this);
31834             this.store.on('loadexception', this.onLoadException, this);
31835             this.store.load({});
31836         }
31837         
31838         
31839         
31840     },
31841
31842     // private
31843     initEvents : function(){
31844         //Roo.form.ComboBox.superclass.initEvents.call(this);
31845  
31846     },
31847
31848     onDestroy : function(){
31849        
31850         if(this.store){
31851             this.store.un('beforeload', this.onBeforeLoad, this);
31852             this.store.un('load', this.onLoad, this);
31853             this.store.un('loadexception', this.onLoadException, this);
31854         }
31855         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31856     },
31857
31858     // private
31859     fireKey : function(e){
31860         if(e.isNavKeyPress() && !this.list.isVisible()){
31861             this.fireEvent("specialkey", this, e);
31862         }
31863     },
31864
31865     // private
31866     onResize: function(w, h){
31867         
31868         return; 
31869     
31870         
31871     },
31872
31873     /**
31874      * Allow or prevent the user from directly editing the field text.  If false is passed,
31875      * the user will only be able to select from the items defined in the dropdown list.  This method
31876      * is the runtime equivalent of setting the 'editable' config option at config time.
31877      * @param {Boolean} value True to allow the user to directly edit the field text
31878      */
31879     setEditable : function(value){
31880          
31881     },
31882
31883     // private
31884     onBeforeLoad : function(){
31885         
31886         Roo.log("Select before load");
31887         return;
31888     
31889         this.innerList.update(this.loadingText ?
31890                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31891         //this.restrictHeight();
31892         this.selectedIndex = -1;
31893     },
31894
31895     // private
31896     onLoad : function(){
31897
31898     
31899         var dom = this.el.dom;
31900         dom.innerHTML = '';
31901          var od = dom.ownerDocument;
31902          
31903         if (this.emptyText) {
31904             var op = od.createElement('option');
31905             op.setAttribute('value', '');
31906             op.innerHTML = String.format('{0}', this.emptyText);
31907             dom.appendChild(op);
31908         }
31909         if(this.store.getCount() > 0){
31910            
31911             var vf = this.valueField;
31912             var df = this.displayField;
31913             this.store.data.each(function(r) {
31914                 // which colmsn to use... testing - cdoe / title..
31915                 var op = od.createElement('option');
31916                 op.setAttribute('value', r.data[vf]);
31917                 op.innerHTML = String.format('{0}', r.data[df]);
31918                 dom.appendChild(op);
31919             });
31920             if (typeof(this.defaultValue != 'undefined')) {
31921                 this.setValue(this.defaultValue);
31922             }
31923             
31924              
31925         }else{
31926             //this.onEmptyResults();
31927         }
31928         //this.el.focus();
31929     },
31930     // private
31931     onLoadException : function()
31932     {
31933         dom.innerHTML = '';
31934             
31935         Roo.log("Select on load exception");
31936         return;
31937     
31938         this.collapse();
31939         Roo.log(this.store.reader.jsonData);
31940         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31941             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31942         }
31943         
31944         
31945     },
31946     // private
31947     onTypeAhead : function(){
31948          
31949     },
31950
31951     // private
31952     onSelect : function(record, index){
31953         Roo.log('on select?');
31954         return;
31955         if(this.fireEvent('beforeselect', this, record, index) !== false){
31956             this.setFromData(index > -1 ? record.data : false);
31957             this.collapse();
31958             this.fireEvent('select', this, record, index);
31959         }
31960     },
31961
31962     /**
31963      * Returns the currently selected field value or empty string if no value is set.
31964      * @return {String} value The selected value
31965      */
31966     getValue : function(){
31967         var dom = this.el.dom;
31968         this.value = dom.options[dom.selectedIndex].value;
31969         return this.value;
31970         
31971     },
31972
31973     /**
31974      * Clears any text/value currently set in the field
31975      */
31976     clearValue : function(){
31977         this.value = '';
31978         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31979         
31980     },
31981
31982     /**
31983      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31984      * will be displayed in the field.  If the value does not match the data value of an existing item,
31985      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31986      * Otherwise the field will be blank (although the value will still be set).
31987      * @param {String} value The value to match
31988      */
31989     setValue : function(v){
31990         var d = this.el.dom;
31991         for (var i =0; i < d.options.length;i++) {
31992             if (v == d.options[i].value) {
31993                 d.selectedIndex = i;
31994                 this.value = v;
31995                 return;
31996             }
31997         }
31998         this.clearValue();
31999     },
32000     /**
32001      * @property {Object} the last set data for the element
32002      */
32003     
32004     lastData : false,
32005     /**
32006      * Sets the value of the field based on a object which is related to the record format for the store.
32007      * @param {Object} value the value to set as. or false on reset?
32008      */
32009     setFromData : function(o){
32010         Roo.log('setfrom data?');
32011          
32012         
32013         
32014     },
32015     // private
32016     reset : function(){
32017         this.clearValue();
32018     },
32019     // private
32020     findRecord : function(prop, value){
32021         
32022         return false;
32023     
32024         var record;
32025         if(this.store.getCount() > 0){
32026             this.store.each(function(r){
32027                 if(r.data[prop] == value){
32028                     record = r;
32029                     return false;
32030                 }
32031                 return true;
32032             });
32033         }
32034         return record;
32035     },
32036     
32037     getName: function()
32038     {
32039         // returns hidden if it's set..
32040         if (!this.rendered) {return ''};
32041         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32042         
32043     },
32044      
32045
32046     
32047
32048     // private
32049     onEmptyResults : function(){
32050         Roo.log('empty results');
32051         //this.collapse();
32052     },
32053
32054     /**
32055      * Returns true if the dropdown list is expanded, else false.
32056      */
32057     isExpanded : function(){
32058         return false;
32059     },
32060
32061     /**
32062      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32063      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32064      * @param {String} value The data value of the item to select
32065      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32066      * selected item if it is not currently in view (defaults to true)
32067      * @return {Boolean} True if the value matched an item in the list, else false
32068      */
32069     selectByValue : function(v, scrollIntoView){
32070         Roo.log('select By Value');
32071         return false;
32072     
32073         if(v !== undefined && v !== null){
32074             var r = this.findRecord(this.valueField || this.displayField, v);
32075             if(r){
32076                 this.select(this.store.indexOf(r), scrollIntoView);
32077                 return true;
32078             }
32079         }
32080         return false;
32081     },
32082
32083     /**
32084      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32085      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32086      * @param {Number} index The zero-based index of the list item to select
32087      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32088      * selected item if it is not currently in view (defaults to true)
32089      */
32090     select : function(index, scrollIntoView){
32091         Roo.log('select ');
32092         return  ;
32093         
32094         this.selectedIndex = index;
32095         this.view.select(index);
32096         if(scrollIntoView !== false){
32097             var el = this.view.getNode(index);
32098             if(el){
32099                 this.innerList.scrollChildIntoView(el, false);
32100             }
32101         }
32102     },
32103
32104       
32105
32106     // private
32107     validateBlur : function(){
32108         
32109         return;
32110         
32111     },
32112
32113     // private
32114     initQuery : function(){
32115         this.doQuery(this.getRawValue());
32116     },
32117
32118     // private
32119     doForce : function(){
32120         if(this.el.dom.value.length > 0){
32121             this.el.dom.value =
32122                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32123              
32124         }
32125     },
32126
32127     /**
32128      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32129      * query allowing the query action to be canceled if needed.
32130      * @param {String} query The SQL query to execute
32131      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32132      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32133      * saved in the current store (defaults to false)
32134      */
32135     doQuery : function(q, forceAll){
32136         
32137         Roo.log('doQuery?');
32138         if(q === undefined || q === null){
32139             q = '';
32140         }
32141         var qe = {
32142             query: q,
32143             forceAll: forceAll,
32144             combo: this,
32145             cancel:false
32146         };
32147         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32148             return false;
32149         }
32150         q = qe.query;
32151         forceAll = qe.forceAll;
32152         if(forceAll === true || (q.length >= this.minChars)){
32153             if(this.lastQuery != q || this.alwaysQuery){
32154                 this.lastQuery = q;
32155                 if(this.mode == 'local'){
32156                     this.selectedIndex = -1;
32157                     if(forceAll){
32158                         this.store.clearFilter();
32159                     }else{
32160                         this.store.filter(this.displayField, q);
32161                     }
32162                     this.onLoad();
32163                 }else{
32164                     this.store.baseParams[this.queryParam] = q;
32165                     this.store.load({
32166                         params: this.getParams(q)
32167                     });
32168                     this.expand();
32169                 }
32170             }else{
32171                 this.selectedIndex = -1;
32172                 this.onLoad();   
32173             }
32174         }
32175     },
32176
32177     // private
32178     getParams : function(q){
32179         var p = {};
32180         //p[this.queryParam] = q;
32181         if(this.pageSize){
32182             p.start = 0;
32183             p.limit = this.pageSize;
32184         }
32185         return p;
32186     },
32187
32188     /**
32189      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32190      */
32191     collapse : function(){
32192         
32193     },
32194
32195     // private
32196     collapseIf : function(e){
32197         
32198     },
32199
32200     /**
32201      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32202      */
32203     expand : function(){
32204         
32205     } ,
32206
32207     // private
32208      
32209
32210     /** 
32211     * @cfg {Boolean} grow 
32212     * @hide 
32213     */
32214     /** 
32215     * @cfg {Number} growMin 
32216     * @hide 
32217     */
32218     /** 
32219     * @cfg {Number} growMax 
32220     * @hide 
32221     */
32222     /**
32223      * @hide
32224      * @method autoSize
32225      */
32226     
32227     setWidth : function()
32228     {
32229         
32230     },
32231     getResizeEl : function(){
32232         return this.el;
32233     }
32234 });//<script type="text/javasscript">
32235  
32236
32237 /**
32238  * @class Roo.DDView
32239  * A DnD enabled version of Roo.View.
32240  * @param {Element/String} container The Element in which to create the View.
32241  * @param {String} tpl The template string used to create the markup for each element of the View
32242  * @param {Object} config The configuration properties. These include all the config options of
32243  * {@link Roo.View} plus some specific to this class.<br>
32244  * <p>
32245  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32246  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32247  * <p>
32248  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32249 .x-view-drag-insert-above {
32250         border-top:1px dotted #3366cc;
32251 }
32252 .x-view-drag-insert-below {
32253         border-bottom:1px dotted #3366cc;
32254 }
32255 </code></pre>
32256  * 
32257  */
32258  
32259 Roo.DDView = function(container, tpl, config) {
32260     Roo.DDView.superclass.constructor.apply(this, arguments);
32261     this.getEl().setStyle("outline", "0px none");
32262     this.getEl().unselectable();
32263     if (this.dragGroup) {
32264                 this.setDraggable(this.dragGroup.split(","));
32265     }
32266     if (this.dropGroup) {
32267                 this.setDroppable(this.dropGroup.split(","));
32268     }
32269     if (this.deletable) {
32270         this.setDeletable();
32271     }
32272     this.isDirtyFlag = false;
32273         this.addEvents({
32274                 "drop" : true
32275         });
32276 };
32277
32278 Roo.extend(Roo.DDView, Roo.View, {
32279 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32280 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32281 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32282 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32283
32284         isFormField: true,
32285
32286         reset: Roo.emptyFn,
32287         
32288         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32289
32290         validate: function() {
32291                 return true;
32292         },
32293         
32294         destroy: function() {
32295                 this.purgeListeners();
32296                 this.getEl.removeAllListeners();
32297                 this.getEl().remove();
32298                 if (this.dragZone) {
32299                         if (this.dragZone.destroy) {
32300                                 this.dragZone.destroy();
32301                         }
32302                 }
32303                 if (this.dropZone) {
32304                         if (this.dropZone.destroy) {
32305                                 this.dropZone.destroy();
32306                         }
32307                 }
32308         },
32309
32310 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32311         getName: function() {
32312                 return this.name;
32313         },
32314
32315 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32316         setValue: function(v) {
32317                 if (!this.store) {
32318                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32319                 }
32320                 var data = {};
32321                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32322                 this.store.proxy = new Roo.data.MemoryProxy(data);
32323                 this.store.load();
32324         },
32325
32326 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32327         getValue: function() {
32328                 var result = '(';
32329                 this.store.each(function(rec) {
32330                         result += rec.id + ',';
32331                 });
32332                 return result.substr(0, result.length - 1) + ')';
32333         },
32334         
32335         getIds: function() {
32336                 var i = 0, result = new Array(this.store.getCount());
32337                 this.store.each(function(rec) {
32338                         result[i++] = rec.id;
32339                 });
32340                 return result;
32341         },
32342         
32343         isDirty: function() {
32344                 return this.isDirtyFlag;
32345         },
32346
32347 /**
32348  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32349  *      whole Element becomes the target, and this causes the drop gesture to append.
32350  */
32351     getTargetFromEvent : function(e) {
32352                 var target = e.getTarget();
32353                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32354                 target = target.parentNode;
32355                 }
32356                 if (!target) {
32357                         target = this.el.dom.lastChild || this.el.dom;
32358                 }
32359                 return target;
32360     },
32361
32362 /**
32363  *      Create the drag data which consists of an object which has the property "ddel" as
32364  *      the drag proxy element. 
32365  */
32366     getDragData : function(e) {
32367         var target = this.findItemFromChild(e.getTarget());
32368                 if(target) {
32369                         this.handleSelection(e);
32370                         var selNodes = this.getSelectedNodes();
32371             var dragData = {
32372                 source: this,
32373                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32374                 nodes: selNodes,
32375                 records: []
32376                         };
32377                         var selectedIndices = this.getSelectedIndexes();
32378                         for (var i = 0; i < selectedIndices.length; i++) {
32379                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32380                         }
32381                         if (selNodes.length == 1) {
32382                                 dragData.ddel = target.cloneNode(true); // the div element
32383                         } else {
32384                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32385                                 div.className = 'multi-proxy';
32386                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32387                                         div.appendChild(selNodes[i].cloneNode(true));
32388                                 }
32389                                 dragData.ddel = div;
32390                         }
32391             //console.log(dragData)
32392             //console.log(dragData.ddel.innerHTML)
32393                         return dragData;
32394                 }
32395         //console.log('nodragData')
32396                 return false;
32397     },
32398     
32399 /**     Specify to which ddGroup items in this DDView may be dragged. */
32400     setDraggable: function(ddGroup) {
32401         if (ddGroup instanceof Array) {
32402                 Roo.each(ddGroup, this.setDraggable, this);
32403                 return;
32404         }
32405         if (this.dragZone) {
32406                 this.dragZone.addToGroup(ddGroup);
32407         } else {
32408                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32409                                 containerScroll: true,
32410                                 ddGroup: ddGroup 
32411
32412                         });
32413 //                      Draggability implies selection. DragZone's mousedown selects the element.
32414                         if (!this.multiSelect) { this.singleSelect = true; }
32415
32416 //                      Wire the DragZone's handlers up to methods in *this*
32417                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32418                 }
32419     },
32420
32421 /**     Specify from which ddGroup this DDView accepts drops. */
32422     setDroppable: function(ddGroup) {
32423         if (ddGroup instanceof Array) {
32424                 Roo.each(ddGroup, this.setDroppable, this);
32425                 return;
32426         }
32427         if (this.dropZone) {
32428                 this.dropZone.addToGroup(ddGroup);
32429         } else {
32430                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32431                                 containerScroll: true,
32432                                 ddGroup: ddGroup
32433                         });
32434
32435 //                      Wire the DropZone's handlers up to methods in *this*
32436                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32437                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32438                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32439                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32440                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32441                 }
32442     },
32443
32444 /**     Decide whether to drop above or below a View node. */
32445     getDropPoint : function(e, n, dd){
32446         if (n == this.el.dom) { return "above"; }
32447                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32448                 var c = t + (b - t) / 2;
32449                 var y = Roo.lib.Event.getPageY(e);
32450                 if(y <= c) {
32451                         return "above";
32452                 }else{
32453                         return "below";
32454                 }
32455     },
32456
32457     onNodeEnter : function(n, dd, e, data){
32458                 return false;
32459     },
32460     
32461     onNodeOver : function(n, dd, e, data){
32462                 var pt = this.getDropPoint(e, n, dd);
32463                 // set the insert point style on the target node
32464                 var dragElClass = this.dropNotAllowed;
32465                 if (pt) {
32466                         var targetElClass;
32467                         if (pt == "above"){
32468                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32469                                 targetElClass = "x-view-drag-insert-above";
32470                         } else {
32471                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32472                                 targetElClass = "x-view-drag-insert-below";
32473                         }
32474                         if (this.lastInsertClass != targetElClass){
32475                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32476                                 this.lastInsertClass = targetElClass;
32477                         }
32478                 }
32479                 return dragElClass;
32480         },
32481
32482     onNodeOut : function(n, dd, e, data){
32483                 this.removeDropIndicators(n);
32484     },
32485
32486     onNodeDrop : function(n, dd, e, data){
32487         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32488                 return false;
32489         }
32490         var pt = this.getDropPoint(e, n, dd);
32491                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32492                 if (pt == "below") { insertAt++; }
32493                 for (var i = 0; i < data.records.length; i++) {
32494                         var r = data.records[i];
32495                         var dup = this.store.getById(r.id);
32496                         if (dup && (dd != this.dragZone)) {
32497                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32498                         } else {
32499                                 if (data.copy) {
32500                                         this.store.insert(insertAt++, r.copy());
32501                                 } else {
32502                                         data.source.isDirtyFlag = true;
32503                                         r.store.remove(r);
32504                                         this.store.insert(insertAt++, r);
32505                                 }
32506                                 this.isDirtyFlag = true;
32507                         }
32508                 }
32509                 this.dragZone.cachedTarget = null;
32510                 return true;
32511     },
32512
32513     removeDropIndicators : function(n){
32514                 if(n){
32515                         Roo.fly(n).removeClass([
32516                                 "x-view-drag-insert-above",
32517                                 "x-view-drag-insert-below"]);
32518                         this.lastInsertClass = "_noclass";
32519                 }
32520     },
32521
32522 /**
32523  *      Utility method. Add a delete option to the DDView's context menu.
32524  *      @param {String} imageUrl The URL of the "delete" icon image.
32525  */
32526         setDeletable: function(imageUrl) {
32527                 if (!this.singleSelect && !this.multiSelect) {
32528                         this.singleSelect = true;
32529                 }
32530                 var c = this.getContextMenu();
32531                 this.contextMenu.on("itemclick", function(item) {
32532                         switch (item.id) {
32533                                 case "delete":
32534                                         this.remove(this.getSelectedIndexes());
32535                                         break;
32536                         }
32537                 }, this);
32538                 this.contextMenu.add({
32539                         icon: imageUrl,
32540                         id: "delete",
32541                         text: 'Delete'
32542                 });
32543         },
32544         
32545 /**     Return the context menu for this DDView. */
32546         getContextMenu: function() {
32547                 if (!this.contextMenu) {
32548 //                      Create the View's context menu
32549                         this.contextMenu = new Roo.menu.Menu({
32550                                 id: this.id + "-contextmenu"
32551                         });
32552                         this.el.on("contextmenu", this.showContextMenu, this);
32553                 }
32554                 return this.contextMenu;
32555         },
32556         
32557         disableContextMenu: function() {
32558                 if (this.contextMenu) {
32559                         this.el.un("contextmenu", this.showContextMenu, this);
32560                 }
32561         },
32562
32563         showContextMenu: function(e, item) {
32564         item = this.findItemFromChild(e.getTarget());
32565                 if (item) {
32566                         e.stopEvent();
32567                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32568                         this.contextMenu.showAt(e.getXY());
32569             }
32570     },
32571
32572 /**
32573  *      Remove {@link Roo.data.Record}s at the specified indices.
32574  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32575  */
32576     remove: function(selectedIndices) {
32577                 selectedIndices = [].concat(selectedIndices);
32578                 for (var i = 0; i < selectedIndices.length; i++) {
32579                         var rec = this.store.getAt(selectedIndices[i]);
32580                         this.store.remove(rec);
32581                 }
32582     },
32583
32584 /**
32585  *      Double click fires the event, but also, if this is draggable, and there is only one other
32586  *      related DropZone, it transfers the selected node.
32587  */
32588     onDblClick : function(e){
32589         var item = this.findItemFromChild(e.getTarget());
32590         if(item){
32591             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32592                 return false;
32593             }
32594             if (this.dragGroup) {
32595                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32596                     while (targets.indexOf(this.dropZone) > -1) {
32597                             targets.remove(this.dropZone);
32598                                 }
32599                     if (targets.length == 1) {
32600                                         this.dragZone.cachedTarget = null;
32601                         var el = Roo.get(targets[0].getEl());
32602                         var box = el.getBox(true);
32603                         targets[0].onNodeDrop(el.dom, {
32604                                 target: el.dom,
32605                                 xy: [box.x, box.y + box.height - 1]
32606                         }, null, this.getDragData(e));
32607                     }
32608                 }
32609         }
32610     },
32611     
32612     handleSelection: function(e) {
32613                 this.dragZone.cachedTarget = null;
32614         var item = this.findItemFromChild(e.getTarget());
32615         if (!item) {
32616                 this.clearSelections(true);
32617                 return;
32618         }
32619                 if (item && (this.multiSelect || this.singleSelect)){
32620                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32621                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32622                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32623                                 this.unselect(item);
32624                         } else {
32625                                 this.select(item, this.multiSelect && e.ctrlKey);
32626                                 this.lastSelection = item;
32627                         }
32628                 }
32629     },
32630
32631     onItemClick : function(item, index, e){
32632                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32633                         return false;
32634                 }
32635                 return true;
32636     },
32637
32638     unselect : function(nodeInfo, suppressEvent){
32639                 var node = this.getNode(nodeInfo);
32640                 if(node && this.isSelected(node)){
32641                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32642                                 Roo.fly(node).removeClass(this.selectedClass);
32643                                 this.selections.remove(node);
32644                                 if(!suppressEvent){
32645                                         this.fireEvent("selectionchange", this, this.selections);
32646                                 }
32647                         }
32648                 }
32649     }
32650 });
32651 /*
32652  * Based on:
32653  * Ext JS Library 1.1.1
32654  * Copyright(c) 2006-2007, Ext JS, LLC.
32655  *
32656  * Originally Released Under LGPL - original licence link has changed is not relivant.
32657  *
32658  * Fork - LGPL
32659  * <script type="text/javascript">
32660  */
32661  
32662 /**
32663  * @class Roo.LayoutManager
32664  * @extends Roo.util.Observable
32665  * Base class for layout managers.
32666  */
32667 Roo.LayoutManager = function(container, config){
32668     Roo.LayoutManager.superclass.constructor.call(this);
32669     this.el = Roo.get(container);
32670     // ie scrollbar fix
32671     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32672         document.body.scroll = "no";
32673     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32674         this.el.position('relative');
32675     }
32676     this.id = this.el.id;
32677     this.el.addClass("x-layout-container");
32678     /** false to disable window resize monitoring @type Boolean */
32679     this.monitorWindowResize = true;
32680     this.regions = {};
32681     this.addEvents({
32682         /**
32683          * @event layout
32684          * Fires when a layout is performed. 
32685          * @param {Roo.LayoutManager} this
32686          */
32687         "layout" : true,
32688         /**
32689          * @event regionresized
32690          * Fires when the user resizes a region. 
32691          * @param {Roo.LayoutRegion} region The resized region
32692          * @param {Number} newSize The new size (width for east/west, height for north/south)
32693          */
32694         "regionresized" : true,
32695         /**
32696          * @event regioncollapsed
32697          * Fires when a region is collapsed. 
32698          * @param {Roo.LayoutRegion} region The collapsed region
32699          */
32700         "regioncollapsed" : true,
32701         /**
32702          * @event regionexpanded
32703          * Fires when a region is expanded.  
32704          * @param {Roo.LayoutRegion} region The expanded region
32705          */
32706         "regionexpanded" : true
32707     });
32708     this.updating = false;
32709     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32710 };
32711
32712 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32713     /**
32714      * Returns true if this layout is currently being updated
32715      * @return {Boolean}
32716      */
32717     isUpdating : function(){
32718         return this.updating; 
32719     },
32720     
32721     /**
32722      * Suspend the LayoutManager from doing auto-layouts while
32723      * making multiple add or remove calls
32724      */
32725     beginUpdate : function(){
32726         this.updating = true;    
32727     },
32728     
32729     /**
32730      * Restore auto-layouts and optionally disable the manager from performing a layout
32731      * @param {Boolean} noLayout true to disable a layout update 
32732      */
32733     endUpdate : function(noLayout){
32734         this.updating = false;
32735         if(!noLayout){
32736             this.layout();
32737         }    
32738     },
32739     
32740     layout: function(){
32741         
32742     },
32743     
32744     onRegionResized : function(region, newSize){
32745         this.fireEvent("regionresized", region, newSize);
32746         this.layout();
32747     },
32748     
32749     onRegionCollapsed : function(region){
32750         this.fireEvent("regioncollapsed", region);
32751     },
32752     
32753     onRegionExpanded : function(region){
32754         this.fireEvent("regionexpanded", region);
32755     },
32756         
32757     /**
32758      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32759      * performs box-model adjustments.
32760      * @return {Object} The size as an object {width: (the width), height: (the height)}
32761      */
32762     getViewSize : function(){
32763         var size;
32764         if(this.el.dom != document.body){
32765             size = this.el.getSize();
32766         }else{
32767             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32768         }
32769         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32770         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32771         return size;
32772     },
32773     
32774     /**
32775      * Returns the Element this layout is bound to.
32776      * @return {Roo.Element}
32777      */
32778     getEl : function(){
32779         return this.el;
32780     },
32781     
32782     /**
32783      * Returns the specified region.
32784      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32785      * @return {Roo.LayoutRegion}
32786      */
32787     getRegion : function(target){
32788         return this.regions[target.toLowerCase()];
32789     },
32790     
32791     onWindowResize : function(){
32792         if(this.monitorWindowResize){
32793             this.layout();
32794         }
32795     }
32796 });/*
32797  * Based on:
32798  * Ext JS Library 1.1.1
32799  * Copyright(c) 2006-2007, Ext JS, LLC.
32800  *
32801  * Originally Released Under LGPL - original licence link has changed is not relivant.
32802  *
32803  * Fork - LGPL
32804  * <script type="text/javascript">
32805  */
32806 /**
32807  * @class Roo.BorderLayout
32808  * @extends Roo.LayoutManager
32809  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32810  * please see: <br><br>
32811  * <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>
32812  * <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>
32813  * Example:
32814  <pre><code>
32815  var layout = new Roo.BorderLayout(document.body, {
32816     north: {
32817         initialSize: 25,
32818         titlebar: false
32819     },
32820     west: {
32821         split:true,
32822         initialSize: 200,
32823         minSize: 175,
32824         maxSize: 400,
32825         titlebar: true,
32826         collapsible: true
32827     },
32828     east: {
32829         split:true,
32830         initialSize: 202,
32831         minSize: 175,
32832         maxSize: 400,
32833         titlebar: true,
32834         collapsible: true
32835     },
32836     south: {
32837         split:true,
32838         initialSize: 100,
32839         minSize: 100,
32840         maxSize: 200,
32841         titlebar: true,
32842         collapsible: true
32843     },
32844     center: {
32845         titlebar: true,
32846         autoScroll:true,
32847         resizeTabs: true,
32848         minTabWidth: 50,
32849         preferredTabWidth: 150
32850     }
32851 });
32852
32853 // shorthand
32854 var CP = Roo.ContentPanel;
32855
32856 layout.beginUpdate();
32857 layout.add("north", new CP("north", "North"));
32858 layout.add("south", new CP("south", {title: "South", closable: true}));
32859 layout.add("west", new CP("west", {title: "West"}));
32860 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32861 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32862 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32863 layout.getRegion("center").showPanel("center1");
32864 layout.endUpdate();
32865 </code></pre>
32866
32867 <b>The container the layout is rendered into can be either the body element or any other element.
32868 If it is not the body element, the container needs to either be an absolute positioned element,
32869 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32870 the container size if it is not the body element.</b>
32871
32872 * @constructor
32873 * Create a new BorderLayout
32874 * @param {String/HTMLElement/Element} container The container this layout is bound to
32875 * @param {Object} config Configuration options
32876  */
32877 Roo.BorderLayout = function(container, config){
32878     config = config || {};
32879     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32880     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32881     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32882         var target = this.factory.validRegions[i];
32883         if(config[target]){
32884             this.addRegion(target, config[target]);
32885         }
32886     }
32887 };
32888
32889 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32890     /**
32891      * Creates and adds a new region if it doesn't already exist.
32892      * @param {String} target The target region key (north, south, east, west or center).
32893      * @param {Object} config The regions config object
32894      * @return {BorderLayoutRegion} The new region
32895      */
32896     addRegion : function(target, config){
32897         if(!this.regions[target]){
32898             var r = this.factory.create(target, this, config);
32899             this.bindRegion(target, r);
32900         }
32901         return this.regions[target];
32902     },
32903
32904     // private (kinda)
32905     bindRegion : function(name, r){
32906         this.regions[name] = r;
32907         r.on("visibilitychange", this.layout, this);
32908         r.on("paneladded", this.layout, this);
32909         r.on("panelremoved", this.layout, this);
32910         r.on("invalidated", this.layout, this);
32911         r.on("resized", this.onRegionResized, this);
32912         r.on("collapsed", this.onRegionCollapsed, this);
32913         r.on("expanded", this.onRegionExpanded, this);
32914     },
32915
32916     /**
32917      * Performs a layout update.
32918      */
32919     layout : function(){
32920         if(this.updating) return;
32921         var size = this.getViewSize();
32922         var w = size.width;
32923         var h = size.height;
32924         var centerW = w;
32925         var centerH = h;
32926         var centerY = 0;
32927         var centerX = 0;
32928         //var x = 0, y = 0;
32929
32930         var rs = this.regions;
32931         var north = rs["north"];
32932         var south = rs["south"]; 
32933         var west = rs["west"];
32934         var east = rs["east"];
32935         var center = rs["center"];
32936         //if(this.hideOnLayout){ // not supported anymore
32937             //c.el.setStyle("display", "none");
32938         //}
32939         if(north && north.isVisible()){
32940             var b = north.getBox();
32941             var m = north.getMargins();
32942             b.width = w - (m.left+m.right);
32943             b.x = m.left;
32944             b.y = m.top;
32945             centerY = b.height + b.y + m.bottom;
32946             centerH -= centerY;
32947             north.updateBox(this.safeBox(b));
32948         }
32949         if(south && south.isVisible()){
32950             var b = south.getBox();
32951             var m = south.getMargins();
32952             b.width = w - (m.left+m.right);
32953             b.x = m.left;
32954             var totalHeight = (b.height + m.top + m.bottom);
32955             b.y = h - totalHeight + m.top;
32956             centerH -= totalHeight;
32957             south.updateBox(this.safeBox(b));
32958         }
32959         if(west && west.isVisible()){
32960             var b = west.getBox();
32961             var m = west.getMargins();
32962             b.height = centerH - (m.top+m.bottom);
32963             b.x = m.left;
32964             b.y = centerY + m.top;
32965             var totalWidth = (b.width + m.left + m.right);
32966             centerX += totalWidth;
32967             centerW -= totalWidth;
32968             west.updateBox(this.safeBox(b));
32969         }
32970         if(east && east.isVisible()){
32971             var b = east.getBox();
32972             var m = east.getMargins();
32973             b.height = centerH - (m.top+m.bottom);
32974             var totalWidth = (b.width + m.left + m.right);
32975             b.x = w - totalWidth + m.left;
32976             b.y = centerY + m.top;
32977             centerW -= totalWidth;
32978             east.updateBox(this.safeBox(b));
32979         }
32980         if(center){
32981             var m = center.getMargins();
32982             var centerBox = {
32983                 x: centerX + m.left,
32984                 y: centerY + m.top,
32985                 width: centerW - (m.left+m.right),
32986                 height: centerH - (m.top+m.bottom)
32987             };
32988             //if(this.hideOnLayout){
32989                 //center.el.setStyle("display", "block");
32990             //}
32991             center.updateBox(this.safeBox(centerBox));
32992         }
32993         this.el.repaint();
32994         this.fireEvent("layout", this);
32995     },
32996
32997     // private
32998     safeBox : function(box){
32999         box.width = Math.max(0, box.width);
33000         box.height = Math.max(0, box.height);
33001         return box;
33002     },
33003
33004     /**
33005      * Adds a ContentPanel (or subclass) to this layout.
33006      * @param {String} target The target region key (north, south, east, west or center).
33007      * @param {Roo.ContentPanel} panel The panel to add
33008      * @return {Roo.ContentPanel} The added panel
33009      */
33010     add : function(target, panel){
33011          
33012         target = target.toLowerCase();
33013         return this.regions[target].add(panel);
33014     },
33015
33016     /**
33017      * Remove a ContentPanel (or subclass) to this layout.
33018      * @param {String} target The target region key (north, south, east, west or center).
33019      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33020      * @return {Roo.ContentPanel} The removed panel
33021      */
33022     remove : function(target, panel){
33023         target = target.toLowerCase();
33024         return this.regions[target].remove(panel);
33025     },
33026
33027     /**
33028      * Searches all regions for a panel with the specified id
33029      * @param {String} panelId
33030      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33031      */
33032     findPanel : function(panelId){
33033         var rs = this.regions;
33034         for(var target in rs){
33035             if(typeof rs[target] != "function"){
33036                 var p = rs[target].getPanel(panelId);
33037                 if(p){
33038                     return p;
33039                 }
33040             }
33041         }
33042         return null;
33043     },
33044
33045     /**
33046      * Searches all regions for a panel with the specified id and activates (shows) it.
33047      * @param {String/ContentPanel} panelId The panels id or the panel itself
33048      * @return {Roo.ContentPanel} The shown panel or null
33049      */
33050     showPanel : function(panelId) {
33051       var rs = this.regions;
33052       for(var target in rs){
33053          var r = rs[target];
33054          if(typeof r != "function"){
33055             if(r.hasPanel(panelId)){
33056                return r.showPanel(panelId);
33057             }
33058          }
33059       }
33060       return null;
33061    },
33062
33063    /**
33064      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33065      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33066      */
33067     restoreState : function(provider){
33068         if(!provider){
33069             provider = Roo.state.Manager;
33070         }
33071         var sm = new Roo.LayoutStateManager();
33072         sm.init(this, provider);
33073     },
33074
33075     /**
33076      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33077      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33078      * a valid ContentPanel config object.  Example:
33079      * <pre><code>
33080 // Create the main layout
33081 var layout = new Roo.BorderLayout('main-ct', {
33082     west: {
33083         split:true,
33084         minSize: 175,
33085         titlebar: true
33086     },
33087     center: {
33088         title:'Components'
33089     }
33090 }, 'main-ct');
33091
33092 // Create and add multiple ContentPanels at once via configs
33093 layout.batchAdd({
33094    west: {
33095        id: 'source-files',
33096        autoCreate:true,
33097        title:'Ext Source Files',
33098        autoScroll:true,
33099        fitToFrame:true
33100    },
33101    center : {
33102        el: cview,
33103        autoScroll:true,
33104        fitToFrame:true,
33105        toolbar: tb,
33106        resizeEl:'cbody'
33107    }
33108 });
33109 </code></pre>
33110      * @param {Object} regions An object containing ContentPanel configs by region name
33111      */
33112     batchAdd : function(regions){
33113         this.beginUpdate();
33114         for(var rname in regions){
33115             var lr = this.regions[rname];
33116             if(lr){
33117                 this.addTypedPanels(lr, regions[rname]);
33118             }
33119         }
33120         this.endUpdate();
33121     },
33122
33123     // private
33124     addTypedPanels : function(lr, ps){
33125         if(typeof ps == 'string'){
33126             lr.add(new Roo.ContentPanel(ps));
33127         }
33128         else if(ps instanceof Array){
33129             for(var i =0, len = ps.length; i < len; i++){
33130                 this.addTypedPanels(lr, ps[i]);
33131             }
33132         }
33133         else if(!ps.events){ // raw config?
33134             var el = ps.el;
33135             delete ps.el; // prevent conflict
33136             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33137         }
33138         else {  // panel object assumed!
33139             lr.add(ps);
33140         }
33141     },
33142     /**
33143      * Adds a xtype elements to the layout.
33144      * <pre><code>
33145
33146 layout.addxtype({
33147        xtype : 'ContentPanel',
33148        region: 'west',
33149        items: [ .... ]
33150    }
33151 );
33152
33153 layout.addxtype({
33154         xtype : 'NestedLayoutPanel',
33155         region: 'west',
33156         layout: {
33157            center: { },
33158            west: { }   
33159         },
33160         items : [ ... list of content panels or nested layout panels.. ]
33161    }
33162 );
33163 </code></pre>
33164      * @param {Object} cfg Xtype definition of item to add.
33165      */
33166     addxtype : function(cfg)
33167     {
33168         // basically accepts a pannel...
33169         // can accept a layout region..!?!?
33170         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33171         
33172         if (!cfg.xtype.match(/Panel$/)) {
33173             return false;
33174         }
33175         var ret = false;
33176         
33177         if (typeof(cfg.region) == 'undefined') {
33178             Roo.log("Failed to add Panel, region was not set");
33179             Roo.log(cfg);
33180             return false;
33181         }
33182         var region = cfg.region;
33183         delete cfg.region;
33184         
33185           
33186         var xitems = [];
33187         if (cfg.items) {
33188             xitems = cfg.items;
33189             delete cfg.items;
33190         }
33191         var nb = false;
33192         
33193         switch(cfg.xtype) 
33194         {
33195             case 'ContentPanel':  // ContentPanel (el, cfg)
33196             case 'ScrollPanel':  // ContentPanel (el, cfg)
33197             case 'ViewPanel': 
33198                 if(cfg.autoCreate) {
33199                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33200                 } else {
33201                     var el = this.el.createChild();
33202                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33203                 }
33204                 
33205                 this.add(region, ret);
33206                 break;
33207             
33208             
33209             case 'TreePanel': // our new panel!
33210                 cfg.el = this.el.createChild();
33211                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33212                 this.add(region, ret);
33213                 break;
33214             
33215             case 'NestedLayoutPanel': 
33216                 // create a new Layout (which is  a Border Layout...
33217                 var el = this.el.createChild();
33218                 var clayout = cfg.layout;
33219                 delete cfg.layout;
33220                 clayout.items   = clayout.items  || [];
33221                 // replace this exitems with the clayout ones..
33222                 xitems = clayout.items;
33223                  
33224                 
33225                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33226                     cfg.background = false;
33227                 }
33228                 var layout = new Roo.BorderLayout(el, clayout);
33229                 
33230                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33231                 //console.log('adding nested layout panel '  + cfg.toSource());
33232                 this.add(region, ret);
33233                 nb = {}; /// find first...
33234                 break;
33235                 
33236             case 'GridPanel': 
33237             
33238                 // needs grid and region
33239                 
33240                 //var el = this.getRegion(region).el.createChild();
33241                 var el = this.el.createChild();
33242                 // create the grid first...
33243                 
33244                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33245                 delete cfg.grid;
33246                 if (region == 'center' && this.active ) {
33247                     cfg.background = false;
33248                 }
33249                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33250                 
33251                 this.add(region, ret);
33252                 if (cfg.background) {
33253                     ret.on('activate', function(gp) {
33254                         if (!gp.grid.rendered) {
33255                             gp.grid.render();
33256                         }
33257                     });
33258                 } else {
33259                     grid.render();
33260                 }
33261                 break;
33262            
33263            
33264            
33265                 
33266                 
33267                 
33268             default:
33269                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33270                     
33271                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33272                     this.add(region, ret);
33273                 } else {
33274                 
33275                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33276                     return null;
33277                 }
33278                 
33279              // GridPanel (grid, cfg)
33280             
33281         }
33282         this.beginUpdate();
33283         // add children..
33284         var region = '';
33285         var abn = {};
33286         Roo.each(xitems, function(i)  {
33287             region = nb && i.region ? i.region : false;
33288             
33289             var add = ret.addxtype(i);
33290            
33291             if (region) {
33292                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33293                 if (!i.background) {
33294                     abn[region] = nb[region] ;
33295                 }
33296             }
33297             
33298         });
33299         this.endUpdate();
33300
33301         // make the last non-background panel active..
33302         //if (nb) { Roo.log(abn); }
33303         if (nb) {
33304             
33305             for(var r in abn) {
33306                 region = this.getRegion(r);
33307                 if (region) {
33308                     // tried using nb[r], but it does not work..
33309                      
33310                     region.showPanel(abn[r]);
33311                    
33312                 }
33313             }
33314         }
33315         return ret;
33316         
33317     }
33318 });
33319
33320 /**
33321  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33322  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33323  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33324  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33325  * <pre><code>
33326 // shorthand
33327 var CP = Roo.ContentPanel;
33328
33329 var layout = Roo.BorderLayout.create({
33330     north: {
33331         initialSize: 25,
33332         titlebar: false,
33333         panels: [new CP("north", "North")]
33334     },
33335     west: {
33336         split:true,
33337         initialSize: 200,
33338         minSize: 175,
33339         maxSize: 400,
33340         titlebar: true,
33341         collapsible: true,
33342         panels: [new CP("west", {title: "West"})]
33343     },
33344     east: {
33345         split:true,
33346         initialSize: 202,
33347         minSize: 175,
33348         maxSize: 400,
33349         titlebar: true,
33350         collapsible: true,
33351         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33352     },
33353     south: {
33354         split:true,
33355         initialSize: 100,
33356         minSize: 100,
33357         maxSize: 200,
33358         titlebar: true,
33359         collapsible: true,
33360         panels: [new CP("south", {title: "South", closable: true})]
33361     },
33362     center: {
33363         titlebar: true,
33364         autoScroll:true,
33365         resizeTabs: true,
33366         minTabWidth: 50,
33367         preferredTabWidth: 150,
33368         panels: [
33369             new CP("center1", {title: "Close Me", closable: true}),
33370             new CP("center2", {title: "Center Panel", closable: false})
33371         ]
33372     }
33373 }, document.body);
33374
33375 layout.getRegion("center").showPanel("center1");
33376 </code></pre>
33377  * @param config
33378  * @param targetEl
33379  */
33380 Roo.BorderLayout.create = function(config, targetEl){
33381     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33382     layout.beginUpdate();
33383     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33384     for(var j = 0, jlen = regions.length; j < jlen; j++){
33385         var lr = regions[j];
33386         if(layout.regions[lr] && config[lr].panels){
33387             var r = layout.regions[lr];
33388             var ps = config[lr].panels;
33389             layout.addTypedPanels(r, ps);
33390         }
33391     }
33392     layout.endUpdate();
33393     return layout;
33394 };
33395
33396 // private
33397 Roo.BorderLayout.RegionFactory = {
33398     // private
33399     validRegions : ["north","south","east","west","center"],
33400
33401     // private
33402     create : function(target, mgr, config){
33403         target = target.toLowerCase();
33404         if(config.lightweight || config.basic){
33405             return new Roo.BasicLayoutRegion(mgr, config, target);
33406         }
33407         switch(target){
33408             case "north":
33409                 return new Roo.NorthLayoutRegion(mgr, config);
33410             case "south":
33411                 return new Roo.SouthLayoutRegion(mgr, config);
33412             case "east":
33413                 return new Roo.EastLayoutRegion(mgr, config);
33414             case "west":
33415                 return new Roo.WestLayoutRegion(mgr, config);
33416             case "center":
33417                 return new Roo.CenterLayoutRegion(mgr, config);
33418         }
33419         throw 'Layout region "'+target+'" not supported.';
33420     }
33421 };/*
33422  * Based on:
33423  * Ext JS Library 1.1.1
33424  * Copyright(c) 2006-2007, Ext JS, LLC.
33425  *
33426  * Originally Released Under LGPL - original licence link has changed is not relivant.
33427  *
33428  * Fork - LGPL
33429  * <script type="text/javascript">
33430  */
33431  
33432 /**
33433  * @class Roo.BasicLayoutRegion
33434  * @extends Roo.util.Observable
33435  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33436  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33437  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33438  */
33439 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33440     this.mgr = mgr;
33441     this.position  = pos;
33442     this.events = {
33443         /**
33444          * @scope Roo.BasicLayoutRegion
33445          */
33446         
33447         /**
33448          * @event beforeremove
33449          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33450          * @param {Roo.LayoutRegion} this
33451          * @param {Roo.ContentPanel} panel The panel
33452          * @param {Object} e The cancel event object
33453          */
33454         "beforeremove" : true,
33455         /**
33456          * @event invalidated
33457          * Fires when the layout for this region is changed.
33458          * @param {Roo.LayoutRegion} this
33459          */
33460         "invalidated" : true,
33461         /**
33462          * @event visibilitychange
33463          * Fires when this region is shown or hidden 
33464          * @param {Roo.LayoutRegion} this
33465          * @param {Boolean} visibility true or false
33466          */
33467         "visibilitychange" : true,
33468         /**
33469          * @event paneladded
33470          * Fires when a panel is added. 
33471          * @param {Roo.LayoutRegion} this
33472          * @param {Roo.ContentPanel} panel The panel
33473          */
33474         "paneladded" : true,
33475         /**
33476          * @event panelremoved
33477          * Fires when a panel is removed. 
33478          * @param {Roo.LayoutRegion} this
33479          * @param {Roo.ContentPanel} panel The panel
33480          */
33481         "panelremoved" : true,
33482         /**
33483          * @event collapsed
33484          * Fires when this region is collapsed.
33485          * @param {Roo.LayoutRegion} this
33486          */
33487         "collapsed" : true,
33488         /**
33489          * @event expanded
33490          * Fires when this region is expanded.
33491          * @param {Roo.LayoutRegion} this
33492          */
33493         "expanded" : true,
33494         /**
33495          * @event slideshow
33496          * Fires when this region is slid into view.
33497          * @param {Roo.LayoutRegion} this
33498          */
33499         "slideshow" : true,
33500         /**
33501          * @event slidehide
33502          * Fires when this region slides out of view. 
33503          * @param {Roo.LayoutRegion} this
33504          */
33505         "slidehide" : true,
33506         /**
33507          * @event panelactivated
33508          * Fires when a panel is activated. 
33509          * @param {Roo.LayoutRegion} this
33510          * @param {Roo.ContentPanel} panel The activated panel
33511          */
33512         "panelactivated" : true,
33513         /**
33514          * @event resized
33515          * Fires when the user resizes this region. 
33516          * @param {Roo.LayoutRegion} this
33517          * @param {Number} newSize The new size (width for east/west, height for north/south)
33518          */
33519         "resized" : true
33520     };
33521     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33522     this.panels = new Roo.util.MixedCollection();
33523     this.panels.getKey = this.getPanelId.createDelegate(this);
33524     this.box = null;
33525     this.activePanel = null;
33526     // ensure listeners are added...
33527     
33528     if (config.listeners || config.events) {
33529         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33530             listeners : config.listeners || {},
33531             events : config.events || {}
33532         });
33533     }
33534     
33535     if(skipConfig !== true){
33536         this.applyConfig(config);
33537     }
33538 };
33539
33540 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33541     getPanelId : function(p){
33542         return p.getId();
33543     },
33544     
33545     applyConfig : function(config){
33546         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33547         this.config = config;
33548         
33549     },
33550     
33551     /**
33552      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33553      * the width, for horizontal (north, south) the height.
33554      * @param {Number} newSize The new width or height
33555      */
33556     resizeTo : function(newSize){
33557         var el = this.el ? this.el :
33558                  (this.activePanel ? this.activePanel.getEl() : null);
33559         if(el){
33560             switch(this.position){
33561                 case "east":
33562                 case "west":
33563                     el.setWidth(newSize);
33564                     this.fireEvent("resized", this, newSize);
33565                 break;
33566                 case "north":
33567                 case "south":
33568                     el.setHeight(newSize);
33569                     this.fireEvent("resized", this, newSize);
33570                 break;                
33571             }
33572         }
33573     },
33574     
33575     getBox : function(){
33576         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33577     },
33578     
33579     getMargins : function(){
33580         return this.margins;
33581     },
33582     
33583     updateBox : function(box){
33584         this.box = box;
33585         var el = this.activePanel.getEl();
33586         el.dom.style.left = box.x + "px";
33587         el.dom.style.top = box.y + "px";
33588         this.activePanel.setSize(box.width, box.height);
33589     },
33590     
33591     /**
33592      * Returns the container element for this region.
33593      * @return {Roo.Element}
33594      */
33595     getEl : function(){
33596         return this.activePanel;
33597     },
33598     
33599     /**
33600      * Returns true if this region is currently visible.
33601      * @return {Boolean}
33602      */
33603     isVisible : function(){
33604         return this.activePanel ? true : false;
33605     },
33606     
33607     setActivePanel : function(panel){
33608         panel = this.getPanel(panel);
33609         if(this.activePanel && this.activePanel != panel){
33610             this.activePanel.setActiveState(false);
33611             this.activePanel.getEl().setLeftTop(-10000,-10000);
33612         }
33613         this.activePanel = panel;
33614         panel.setActiveState(true);
33615         if(this.box){
33616             panel.setSize(this.box.width, this.box.height);
33617         }
33618         this.fireEvent("panelactivated", this, panel);
33619         this.fireEvent("invalidated");
33620     },
33621     
33622     /**
33623      * Show the specified panel.
33624      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33625      * @return {Roo.ContentPanel} The shown panel or null
33626      */
33627     showPanel : function(panel){
33628         if(panel = this.getPanel(panel)){
33629             this.setActivePanel(panel);
33630         }
33631         return panel;
33632     },
33633     
33634     /**
33635      * Get the active panel for this region.
33636      * @return {Roo.ContentPanel} The active panel or null
33637      */
33638     getActivePanel : function(){
33639         return this.activePanel;
33640     },
33641     
33642     /**
33643      * Add the passed ContentPanel(s)
33644      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33645      * @return {Roo.ContentPanel} The panel added (if only one was added)
33646      */
33647     add : function(panel){
33648         if(arguments.length > 1){
33649             for(var i = 0, len = arguments.length; i < len; i++) {
33650                 this.add(arguments[i]);
33651             }
33652             return null;
33653         }
33654         if(this.hasPanel(panel)){
33655             this.showPanel(panel);
33656             return panel;
33657         }
33658         var el = panel.getEl();
33659         if(el.dom.parentNode != this.mgr.el.dom){
33660             this.mgr.el.dom.appendChild(el.dom);
33661         }
33662         if(panel.setRegion){
33663             panel.setRegion(this);
33664         }
33665         this.panels.add(panel);
33666         el.setStyle("position", "absolute");
33667         if(!panel.background){
33668             this.setActivePanel(panel);
33669             if(this.config.initialSize && this.panels.getCount()==1){
33670                 this.resizeTo(this.config.initialSize);
33671             }
33672         }
33673         this.fireEvent("paneladded", this, panel);
33674         return panel;
33675     },
33676     
33677     /**
33678      * Returns true if the panel is in this region.
33679      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33680      * @return {Boolean}
33681      */
33682     hasPanel : function(panel){
33683         if(typeof panel == "object"){ // must be panel obj
33684             panel = panel.getId();
33685         }
33686         return this.getPanel(panel) ? true : false;
33687     },
33688     
33689     /**
33690      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33691      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33692      * @param {Boolean} preservePanel Overrides the config preservePanel option
33693      * @return {Roo.ContentPanel} The panel that was removed
33694      */
33695     remove : function(panel, preservePanel){
33696         panel = this.getPanel(panel);
33697         if(!panel){
33698             return null;
33699         }
33700         var e = {};
33701         this.fireEvent("beforeremove", this, panel, e);
33702         if(e.cancel === true){
33703             return null;
33704         }
33705         var panelId = panel.getId();
33706         this.panels.removeKey(panelId);
33707         return panel;
33708     },
33709     
33710     /**
33711      * Returns the panel specified or null if it's not in this region.
33712      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33713      * @return {Roo.ContentPanel}
33714      */
33715     getPanel : function(id){
33716         if(typeof id == "object"){ // must be panel obj
33717             return id;
33718         }
33719         return this.panels.get(id);
33720     },
33721     
33722     /**
33723      * Returns this regions position (north/south/east/west/center).
33724      * @return {String} 
33725      */
33726     getPosition: function(){
33727         return this.position;    
33728     }
33729 });/*
33730  * Based on:
33731  * Ext JS Library 1.1.1
33732  * Copyright(c) 2006-2007, Ext JS, LLC.
33733  *
33734  * Originally Released Under LGPL - original licence link has changed is not relivant.
33735  *
33736  * Fork - LGPL
33737  * <script type="text/javascript">
33738  */
33739  
33740 /**
33741  * @class Roo.LayoutRegion
33742  * @extends Roo.BasicLayoutRegion
33743  * This class represents a region in a layout manager.
33744  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33745  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33746  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33747  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33748  * @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})
33749  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33750  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33751  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33752  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33753  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33754  * @cfg {String}    title           The title for the region (overrides panel titles)
33755  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33756  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33757  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33758  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33759  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33760  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33761  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33762  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33763  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33764  * @cfg {Boolean}   showPin         True to show a pin button
33765  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33766  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33767  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33768  * @cfg {Number}    width           For East/West panels
33769  * @cfg {Number}    height          For North/South panels
33770  * @cfg {Boolean}   split           To show the splitter
33771  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33772  */
33773 Roo.LayoutRegion = function(mgr, config, pos){
33774     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33775     var dh = Roo.DomHelper;
33776     /** This region's container element 
33777     * @type Roo.Element */
33778     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33779     /** This region's title element 
33780     * @type Roo.Element */
33781
33782     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33783         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33784         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33785     ]}, true);
33786     this.titleEl.enableDisplayMode();
33787     /** This region's title text element 
33788     * @type HTMLElement */
33789     this.titleTextEl = this.titleEl.dom.firstChild;
33790     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33791     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33792     this.closeBtn.enableDisplayMode();
33793     this.closeBtn.on("click", this.closeClicked, this);
33794     this.closeBtn.hide();
33795
33796     this.createBody(config);
33797     this.visible = true;
33798     this.collapsed = false;
33799
33800     if(config.hideWhenEmpty){
33801         this.hide();
33802         this.on("paneladded", this.validateVisibility, this);
33803         this.on("panelremoved", this.validateVisibility, this);
33804     }
33805     this.applyConfig(config);
33806 };
33807
33808 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33809
33810     createBody : function(){
33811         /** This region's body element 
33812         * @type Roo.Element */
33813         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33814     },
33815
33816     applyConfig : function(c){
33817         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33818             var dh = Roo.DomHelper;
33819             if(c.titlebar !== false){
33820                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33821                 this.collapseBtn.on("click", this.collapse, this);
33822                 this.collapseBtn.enableDisplayMode();
33823
33824                 if(c.showPin === true || this.showPin){
33825                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33826                     this.stickBtn.enableDisplayMode();
33827                     this.stickBtn.on("click", this.expand, this);
33828                     this.stickBtn.hide();
33829                 }
33830             }
33831             /** This region's collapsed element
33832             * @type Roo.Element */
33833             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33834                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33835             ]}, true);
33836             if(c.floatable !== false){
33837                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33838                this.collapsedEl.on("click", this.collapseClick, this);
33839             }
33840
33841             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33842                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33843                    id: "message", unselectable: "on", style:{"float":"left"}});
33844                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33845              }
33846             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33847             this.expandBtn.on("click", this.expand, this);
33848         }
33849         if(this.collapseBtn){
33850             this.collapseBtn.setVisible(c.collapsible == true);
33851         }
33852         this.cmargins = c.cmargins || this.cmargins ||
33853                          (this.position == "west" || this.position == "east" ?
33854                              {top: 0, left: 2, right:2, bottom: 0} :
33855                              {top: 2, left: 0, right:0, bottom: 2});
33856         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33857         this.bottomTabs = c.tabPosition != "top";
33858         this.autoScroll = c.autoScroll || false;
33859         if(this.autoScroll){
33860             this.bodyEl.setStyle("overflow", "auto");
33861         }else{
33862             this.bodyEl.setStyle("overflow", "hidden");
33863         }
33864         //if(c.titlebar !== false){
33865             if((!c.titlebar && !c.title) || c.titlebar === false){
33866                 this.titleEl.hide();
33867             }else{
33868                 this.titleEl.show();
33869                 if(c.title){
33870                     this.titleTextEl.innerHTML = c.title;
33871                 }
33872             }
33873         //}
33874         this.duration = c.duration || .30;
33875         this.slideDuration = c.slideDuration || .45;
33876         this.config = c;
33877         if(c.collapsed){
33878             this.collapse(true);
33879         }
33880         if(c.hidden){
33881             this.hide();
33882         }
33883     },
33884     /**
33885      * Returns true if this region is currently visible.
33886      * @return {Boolean}
33887      */
33888     isVisible : function(){
33889         return this.visible;
33890     },
33891
33892     /**
33893      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33894      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33895      */
33896     setCollapsedTitle : function(title){
33897         title = title || "&#160;";
33898         if(this.collapsedTitleTextEl){
33899             this.collapsedTitleTextEl.innerHTML = title;
33900         }
33901     },
33902
33903     getBox : function(){
33904         var b;
33905         if(!this.collapsed){
33906             b = this.el.getBox(false, true);
33907         }else{
33908             b = this.collapsedEl.getBox(false, true);
33909         }
33910         return b;
33911     },
33912
33913     getMargins : function(){
33914         return this.collapsed ? this.cmargins : this.margins;
33915     },
33916
33917     highlight : function(){
33918         this.el.addClass("x-layout-panel-dragover");
33919     },
33920
33921     unhighlight : function(){
33922         this.el.removeClass("x-layout-panel-dragover");
33923     },
33924
33925     updateBox : function(box){
33926         this.box = box;
33927         if(!this.collapsed){
33928             this.el.dom.style.left = box.x + "px";
33929             this.el.dom.style.top = box.y + "px";
33930             this.updateBody(box.width, box.height);
33931         }else{
33932             this.collapsedEl.dom.style.left = box.x + "px";
33933             this.collapsedEl.dom.style.top = box.y + "px";
33934             this.collapsedEl.setSize(box.width, box.height);
33935         }
33936         if(this.tabs){
33937             this.tabs.autoSizeTabs();
33938         }
33939     },
33940
33941     updateBody : function(w, h){
33942         if(w !== null){
33943             this.el.setWidth(w);
33944             w -= this.el.getBorderWidth("rl");
33945             if(this.config.adjustments){
33946                 w += this.config.adjustments[0];
33947             }
33948         }
33949         if(h !== null){
33950             this.el.setHeight(h);
33951             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33952             h -= this.el.getBorderWidth("tb");
33953             if(this.config.adjustments){
33954                 h += this.config.adjustments[1];
33955             }
33956             this.bodyEl.setHeight(h);
33957             if(this.tabs){
33958                 h = this.tabs.syncHeight(h);
33959             }
33960         }
33961         if(this.panelSize){
33962             w = w !== null ? w : this.panelSize.width;
33963             h = h !== null ? h : this.panelSize.height;
33964         }
33965         if(this.activePanel){
33966             var el = this.activePanel.getEl();
33967             w = w !== null ? w : el.getWidth();
33968             h = h !== null ? h : el.getHeight();
33969             this.panelSize = {width: w, height: h};
33970             this.activePanel.setSize(w, h);
33971         }
33972         if(Roo.isIE && this.tabs){
33973             this.tabs.el.repaint();
33974         }
33975     },
33976
33977     /**
33978      * Returns the container element for this region.
33979      * @return {Roo.Element}
33980      */
33981     getEl : function(){
33982         return this.el;
33983     },
33984
33985     /**
33986      * Hides this region.
33987      */
33988     hide : function(){
33989         if(!this.collapsed){
33990             this.el.dom.style.left = "-2000px";
33991             this.el.hide();
33992         }else{
33993             this.collapsedEl.dom.style.left = "-2000px";
33994             this.collapsedEl.hide();
33995         }
33996         this.visible = false;
33997         this.fireEvent("visibilitychange", this, false);
33998     },
33999
34000     /**
34001      * Shows this region if it was previously hidden.
34002      */
34003     show : function(){
34004         if(!this.collapsed){
34005             this.el.show();
34006         }else{
34007             this.collapsedEl.show();
34008         }
34009         this.visible = true;
34010         this.fireEvent("visibilitychange", this, true);
34011     },
34012
34013     closeClicked : function(){
34014         if(this.activePanel){
34015             this.remove(this.activePanel);
34016         }
34017     },
34018
34019     collapseClick : function(e){
34020         if(this.isSlid){
34021            e.stopPropagation();
34022            this.slideIn();
34023         }else{
34024            e.stopPropagation();
34025            this.slideOut();
34026         }
34027     },
34028
34029     /**
34030      * Collapses this region.
34031      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34032      */
34033     collapse : function(skipAnim){
34034         if(this.collapsed) return;
34035         this.collapsed = true;
34036         if(this.split){
34037             this.split.el.hide();
34038         }
34039         if(this.config.animate && skipAnim !== true){
34040             this.fireEvent("invalidated", this);
34041             this.animateCollapse();
34042         }else{
34043             this.el.setLocation(-20000,-20000);
34044             this.el.hide();
34045             this.collapsedEl.show();
34046             this.fireEvent("collapsed", this);
34047             this.fireEvent("invalidated", this);
34048         }
34049     },
34050
34051     animateCollapse : function(){
34052         // overridden
34053     },
34054
34055     /**
34056      * Expands this region if it was previously collapsed.
34057      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34058      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34059      */
34060     expand : function(e, skipAnim){
34061         if(e) e.stopPropagation();
34062         if(!this.collapsed || this.el.hasActiveFx()) return;
34063         if(this.isSlid){
34064             this.afterSlideIn();
34065             skipAnim = true;
34066         }
34067         this.collapsed = false;
34068         if(this.config.animate && skipAnim !== true){
34069             this.animateExpand();
34070         }else{
34071             this.el.show();
34072             if(this.split){
34073                 this.split.el.show();
34074             }
34075             this.collapsedEl.setLocation(-2000,-2000);
34076             this.collapsedEl.hide();
34077             this.fireEvent("invalidated", this);
34078             this.fireEvent("expanded", this);
34079         }
34080     },
34081
34082     animateExpand : function(){
34083         // overridden
34084     },
34085
34086     initTabs : function()
34087     {
34088         this.bodyEl.setStyle("overflow", "hidden");
34089         var ts = new Roo.TabPanel(
34090                 this.bodyEl.dom,
34091                 {
34092                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34093                     disableTooltips: this.config.disableTabTips,
34094                     toolbar : this.config.toolbar
34095                 }
34096         );
34097         if(this.config.hideTabs){
34098             ts.stripWrap.setDisplayed(false);
34099         }
34100         this.tabs = ts;
34101         ts.resizeTabs = this.config.resizeTabs === true;
34102         ts.minTabWidth = this.config.minTabWidth || 40;
34103         ts.maxTabWidth = this.config.maxTabWidth || 250;
34104         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34105         ts.monitorResize = false;
34106         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34107         ts.bodyEl.addClass('x-layout-tabs-body');
34108         this.panels.each(this.initPanelAsTab, this);
34109     },
34110
34111     initPanelAsTab : function(panel){
34112         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34113                     this.config.closeOnTab && panel.isClosable());
34114         if(panel.tabTip !== undefined){
34115             ti.setTooltip(panel.tabTip);
34116         }
34117         ti.on("activate", function(){
34118               this.setActivePanel(panel);
34119         }, this);
34120         if(this.config.closeOnTab){
34121             ti.on("beforeclose", function(t, e){
34122                 e.cancel = true;
34123                 this.remove(panel);
34124             }, this);
34125         }
34126         return ti;
34127     },
34128
34129     updatePanelTitle : function(panel, title){
34130         if(this.activePanel == panel){
34131             this.updateTitle(title);
34132         }
34133         if(this.tabs){
34134             var ti = this.tabs.getTab(panel.getEl().id);
34135             ti.setText(title);
34136             if(panel.tabTip !== undefined){
34137                 ti.setTooltip(panel.tabTip);
34138             }
34139         }
34140     },
34141
34142     updateTitle : function(title){
34143         if(this.titleTextEl && !this.config.title){
34144             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34145         }
34146     },
34147
34148     setActivePanel : function(panel){
34149         panel = this.getPanel(panel);
34150         if(this.activePanel && this.activePanel != panel){
34151             this.activePanel.setActiveState(false);
34152         }
34153         this.activePanel = panel;
34154         panel.setActiveState(true);
34155         if(this.panelSize){
34156             panel.setSize(this.panelSize.width, this.panelSize.height);
34157         }
34158         if(this.closeBtn){
34159             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34160         }
34161         this.updateTitle(panel.getTitle());
34162         if(this.tabs){
34163             this.fireEvent("invalidated", this);
34164         }
34165         this.fireEvent("panelactivated", this, panel);
34166     },
34167
34168     /**
34169      * Shows the specified panel.
34170      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34171      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34172      */
34173     showPanel : function(panel)
34174     {
34175         panel = this.getPanel(panel);
34176         if(panel){
34177             if(this.tabs){
34178                 var tab = this.tabs.getTab(panel.getEl().id);
34179                 if(tab.isHidden()){
34180                     this.tabs.unhideTab(tab.id);
34181                 }
34182                 tab.activate();
34183             }else{
34184                 this.setActivePanel(panel);
34185             }
34186         }
34187         return panel;
34188     },
34189
34190     /**
34191      * Get the active panel for this region.
34192      * @return {Roo.ContentPanel} The active panel or null
34193      */
34194     getActivePanel : function(){
34195         return this.activePanel;
34196     },
34197
34198     validateVisibility : function(){
34199         if(this.panels.getCount() < 1){
34200             this.updateTitle("&#160;");
34201             this.closeBtn.hide();
34202             this.hide();
34203         }else{
34204             if(!this.isVisible()){
34205                 this.show();
34206             }
34207         }
34208     },
34209
34210     /**
34211      * Adds the passed ContentPanel(s) to this region.
34212      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34213      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34214      */
34215     add : function(panel){
34216         if(arguments.length > 1){
34217             for(var i = 0, len = arguments.length; i < len; i++) {
34218                 this.add(arguments[i]);
34219             }
34220             return null;
34221         }
34222         if(this.hasPanel(panel)){
34223             this.showPanel(panel);
34224             return panel;
34225         }
34226         panel.setRegion(this);
34227         this.panels.add(panel);
34228         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34229             this.bodyEl.dom.appendChild(panel.getEl().dom);
34230             if(panel.background !== true){
34231                 this.setActivePanel(panel);
34232             }
34233             this.fireEvent("paneladded", this, panel);
34234             return panel;
34235         }
34236         if(!this.tabs){
34237             this.initTabs();
34238         }else{
34239             this.initPanelAsTab(panel);
34240         }
34241         if(panel.background !== true){
34242             this.tabs.activate(panel.getEl().id);
34243         }
34244         this.fireEvent("paneladded", this, panel);
34245         return panel;
34246     },
34247
34248     /**
34249      * Hides the tab for the specified panel.
34250      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34251      */
34252     hidePanel : function(panel){
34253         if(this.tabs && (panel = this.getPanel(panel))){
34254             this.tabs.hideTab(panel.getEl().id);
34255         }
34256     },
34257
34258     /**
34259      * Unhides the tab for a previously hidden panel.
34260      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34261      */
34262     unhidePanel : function(panel){
34263         if(this.tabs && (panel = this.getPanel(panel))){
34264             this.tabs.unhideTab(panel.getEl().id);
34265         }
34266     },
34267
34268     clearPanels : function(){
34269         while(this.panels.getCount() > 0){
34270              this.remove(this.panels.first());
34271         }
34272     },
34273
34274     /**
34275      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34276      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34277      * @param {Boolean} preservePanel Overrides the config preservePanel option
34278      * @return {Roo.ContentPanel} The panel that was removed
34279      */
34280     remove : function(panel, preservePanel){
34281         panel = this.getPanel(panel);
34282         if(!panel){
34283             return null;
34284         }
34285         var e = {};
34286         this.fireEvent("beforeremove", this, panel, e);
34287         if(e.cancel === true){
34288             return null;
34289         }
34290         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34291         var panelId = panel.getId();
34292         this.panels.removeKey(panelId);
34293         if(preservePanel){
34294             document.body.appendChild(panel.getEl().dom);
34295         }
34296         if(this.tabs){
34297             this.tabs.removeTab(panel.getEl().id);
34298         }else if (!preservePanel){
34299             this.bodyEl.dom.removeChild(panel.getEl().dom);
34300         }
34301         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34302             var p = this.panels.first();
34303             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34304             tempEl.appendChild(p.getEl().dom);
34305             this.bodyEl.update("");
34306             this.bodyEl.dom.appendChild(p.getEl().dom);
34307             tempEl = null;
34308             this.updateTitle(p.getTitle());
34309             this.tabs = null;
34310             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34311             this.setActivePanel(p);
34312         }
34313         panel.setRegion(null);
34314         if(this.activePanel == panel){
34315             this.activePanel = null;
34316         }
34317         if(this.config.autoDestroy !== false && preservePanel !== true){
34318             try{panel.destroy();}catch(e){}
34319         }
34320         this.fireEvent("panelremoved", this, panel);
34321         return panel;
34322     },
34323
34324     /**
34325      * Returns the TabPanel component used by this region
34326      * @return {Roo.TabPanel}
34327      */
34328     getTabs : function(){
34329         return this.tabs;
34330     },
34331
34332     createTool : function(parentEl, className){
34333         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34334             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34335         btn.addClassOnOver("x-layout-tools-button-over");
34336         return btn;
34337     }
34338 });/*
34339  * Based on:
34340  * Ext JS Library 1.1.1
34341  * Copyright(c) 2006-2007, Ext JS, LLC.
34342  *
34343  * Originally Released Under LGPL - original licence link has changed is not relivant.
34344  *
34345  * Fork - LGPL
34346  * <script type="text/javascript">
34347  */
34348  
34349
34350
34351 /**
34352  * @class Roo.SplitLayoutRegion
34353  * @extends Roo.LayoutRegion
34354  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34355  */
34356 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34357     this.cursor = cursor;
34358     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34359 };
34360
34361 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34362     splitTip : "Drag to resize.",
34363     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34364     useSplitTips : false,
34365
34366     applyConfig : function(config){
34367         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34368         if(config.split){
34369             if(!this.split){
34370                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34371                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34372                 /** The SplitBar for this region 
34373                 * @type Roo.SplitBar */
34374                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34375                 this.split.on("moved", this.onSplitMove, this);
34376                 this.split.useShim = config.useShim === true;
34377                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34378                 if(this.useSplitTips){
34379                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34380                 }
34381                 if(config.collapsible){
34382                     this.split.el.on("dblclick", this.collapse,  this);
34383                 }
34384             }
34385             if(typeof config.minSize != "undefined"){
34386                 this.split.minSize = config.minSize;
34387             }
34388             if(typeof config.maxSize != "undefined"){
34389                 this.split.maxSize = config.maxSize;
34390             }
34391             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34392                 this.hideSplitter();
34393             }
34394         }
34395     },
34396
34397     getHMaxSize : function(){
34398          var cmax = this.config.maxSize || 10000;
34399          var center = this.mgr.getRegion("center");
34400          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34401     },
34402
34403     getVMaxSize : function(){
34404          var cmax = this.config.maxSize || 10000;
34405          var center = this.mgr.getRegion("center");
34406          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34407     },
34408
34409     onSplitMove : function(split, newSize){
34410         this.fireEvent("resized", this, newSize);
34411     },
34412     
34413     /** 
34414      * Returns the {@link Roo.SplitBar} for this region.
34415      * @return {Roo.SplitBar}
34416      */
34417     getSplitBar : function(){
34418         return this.split;
34419     },
34420     
34421     hide : function(){
34422         this.hideSplitter();
34423         Roo.SplitLayoutRegion.superclass.hide.call(this);
34424     },
34425
34426     hideSplitter : function(){
34427         if(this.split){
34428             this.split.el.setLocation(-2000,-2000);
34429             this.split.el.hide();
34430         }
34431     },
34432
34433     show : function(){
34434         if(this.split){
34435             this.split.el.show();
34436         }
34437         Roo.SplitLayoutRegion.superclass.show.call(this);
34438     },
34439     
34440     beforeSlide: function(){
34441         if(Roo.isGecko){// firefox overflow auto bug workaround
34442             this.bodyEl.clip();
34443             if(this.tabs) this.tabs.bodyEl.clip();
34444             if(this.activePanel){
34445                 this.activePanel.getEl().clip();
34446                 
34447                 if(this.activePanel.beforeSlide){
34448                     this.activePanel.beforeSlide();
34449                 }
34450             }
34451         }
34452     },
34453     
34454     afterSlide : function(){
34455         if(Roo.isGecko){// firefox overflow auto bug workaround
34456             this.bodyEl.unclip();
34457             if(this.tabs) this.tabs.bodyEl.unclip();
34458             if(this.activePanel){
34459                 this.activePanel.getEl().unclip();
34460                 if(this.activePanel.afterSlide){
34461                     this.activePanel.afterSlide();
34462                 }
34463             }
34464         }
34465     },
34466
34467     initAutoHide : function(){
34468         if(this.autoHide !== false){
34469             if(!this.autoHideHd){
34470                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34471                 this.autoHideHd = {
34472                     "mouseout": function(e){
34473                         if(!e.within(this.el, true)){
34474                             st.delay(500);
34475                         }
34476                     },
34477                     "mouseover" : function(e){
34478                         st.cancel();
34479                     },
34480                     scope : this
34481                 };
34482             }
34483             this.el.on(this.autoHideHd);
34484         }
34485     },
34486
34487     clearAutoHide : function(){
34488         if(this.autoHide !== false){
34489             this.el.un("mouseout", this.autoHideHd.mouseout);
34490             this.el.un("mouseover", this.autoHideHd.mouseover);
34491         }
34492     },
34493
34494     clearMonitor : function(){
34495         Roo.get(document).un("click", this.slideInIf, this);
34496     },
34497
34498     // these names are backwards but not changed for compat
34499     slideOut : function(){
34500         if(this.isSlid || this.el.hasActiveFx()){
34501             return;
34502         }
34503         this.isSlid = true;
34504         if(this.collapseBtn){
34505             this.collapseBtn.hide();
34506         }
34507         this.closeBtnState = this.closeBtn.getStyle('display');
34508         this.closeBtn.hide();
34509         if(this.stickBtn){
34510             this.stickBtn.show();
34511         }
34512         this.el.show();
34513         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34514         this.beforeSlide();
34515         this.el.setStyle("z-index", 10001);
34516         this.el.slideIn(this.getSlideAnchor(), {
34517             callback: function(){
34518                 this.afterSlide();
34519                 this.initAutoHide();
34520                 Roo.get(document).on("click", this.slideInIf, this);
34521                 this.fireEvent("slideshow", this);
34522             },
34523             scope: this,
34524             block: true
34525         });
34526     },
34527
34528     afterSlideIn : function(){
34529         this.clearAutoHide();
34530         this.isSlid = false;
34531         this.clearMonitor();
34532         this.el.setStyle("z-index", "");
34533         if(this.collapseBtn){
34534             this.collapseBtn.show();
34535         }
34536         this.closeBtn.setStyle('display', this.closeBtnState);
34537         if(this.stickBtn){
34538             this.stickBtn.hide();
34539         }
34540         this.fireEvent("slidehide", this);
34541     },
34542
34543     slideIn : function(cb){
34544         if(!this.isSlid || this.el.hasActiveFx()){
34545             Roo.callback(cb);
34546             return;
34547         }
34548         this.isSlid = false;
34549         this.beforeSlide();
34550         this.el.slideOut(this.getSlideAnchor(), {
34551             callback: function(){
34552                 this.el.setLeftTop(-10000, -10000);
34553                 this.afterSlide();
34554                 this.afterSlideIn();
34555                 Roo.callback(cb);
34556             },
34557             scope: this,
34558             block: true
34559         });
34560     },
34561     
34562     slideInIf : function(e){
34563         if(!e.within(this.el)){
34564             this.slideIn();
34565         }
34566     },
34567
34568     animateCollapse : function(){
34569         this.beforeSlide();
34570         this.el.setStyle("z-index", 20000);
34571         var anchor = this.getSlideAnchor();
34572         this.el.slideOut(anchor, {
34573             callback : function(){
34574                 this.el.setStyle("z-index", "");
34575                 this.collapsedEl.slideIn(anchor, {duration:.3});
34576                 this.afterSlide();
34577                 this.el.setLocation(-10000,-10000);
34578                 this.el.hide();
34579                 this.fireEvent("collapsed", this);
34580             },
34581             scope: this,
34582             block: true
34583         });
34584     },
34585
34586     animateExpand : function(){
34587         this.beforeSlide();
34588         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34589         this.el.setStyle("z-index", 20000);
34590         this.collapsedEl.hide({
34591             duration:.1
34592         });
34593         this.el.slideIn(this.getSlideAnchor(), {
34594             callback : function(){
34595                 this.el.setStyle("z-index", "");
34596                 this.afterSlide();
34597                 if(this.split){
34598                     this.split.el.show();
34599                 }
34600                 this.fireEvent("invalidated", this);
34601                 this.fireEvent("expanded", this);
34602             },
34603             scope: this,
34604             block: true
34605         });
34606     },
34607
34608     anchors : {
34609         "west" : "left",
34610         "east" : "right",
34611         "north" : "top",
34612         "south" : "bottom"
34613     },
34614
34615     sanchors : {
34616         "west" : "l",
34617         "east" : "r",
34618         "north" : "t",
34619         "south" : "b"
34620     },
34621
34622     canchors : {
34623         "west" : "tl-tr",
34624         "east" : "tr-tl",
34625         "north" : "tl-bl",
34626         "south" : "bl-tl"
34627     },
34628
34629     getAnchor : function(){
34630         return this.anchors[this.position];
34631     },
34632
34633     getCollapseAnchor : function(){
34634         return this.canchors[this.position];
34635     },
34636
34637     getSlideAnchor : function(){
34638         return this.sanchors[this.position];
34639     },
34640
34641     getAlignAdj : function(){
34642         var cm = this.cmargins;
34643         switch(this.position){
34644             case "west":
34645                 return [0, 0];
34646             break;
34647             case "east":
34648                 return [0, 0];
34649             break;
34650             case "north":
34651                 return [0, 0];
34652             break;
34653             case "south":
34654                 return [0, 0];
34655             break;
34656         }
34657     },
34658
34659     getExpandAdj : function(){
34660         var c = this.collapsedEl, cm = this.cmargins;
34661         switch(this.position){
34662             case "west":
34663                 return [-(cm.right+c.getWidth()+cm.left), 0];
34664             break;
34665             case "east":
34666                 return [cm.right+c.getWidth()+cm.left, 0];
34667             break;
34668             case "north":
34669                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34670             break;
34671             case "south":
34672                 return [0, cm.top+cm.bottom+c.getHeight()];
34673             break;
34674         }
34675     }
34676 });/*
34677  * Based on:
34678  * Ext JS Library 1.1.1
34679  * Copyright(c) 2006-2007, Ext JS, LLC.
34680  *
34681  * Originally Released Under LGPL - original licence link has changed is not relivant.
34682  *
34683  * Fork - LGPL
34684  * <script type="text/javascript">
34685  */
34686 /*
34687  * These classes are private internal classes
34688  */
34689 Roo.CenterLayoutRegion = function(mgr, config){
34690     Roo.LayoutRegion.call(this, mgr, config, "center");
34691     this.visible = true;
34692     this.minWidth = config.minWidth || 20;
34693     this.minHeight = config.minHeight || 20;
34694 };
34695
34696 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34697     hide : function(){
34698         // center panel can't be hidden
34699     },
34700     
34701     show : function(){
34702         // center panel can't be hidden
34703     },
34704     
34705     getMinWidth: function(){
34706         return this.minWidth;
34707     },
34708     
34709     getMinHeight: function(){
34710         return this.minHeight;
34711     }
34712 });
34713
34714
34715 Roo.NorthLayoutRegion = function(mgr, config){
34716     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34717     if(this.split){
34718         this.split.placement = Roo.SplitBar.TOP;
34719         this.split.orientation = Roo.SplitBar.VERTICAL;
34720         this.split.el.addClass("x-layout-split-v");
34721     }
34722     var size = config.initialSize || config.height;
34723     if(typeof size != "undefined"){
34724         this.el.setHeight(size);
34725     }
34726 };
34727 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34728     orientation: Roo.SplitBar.VERTICAL,
34729     getBox : function(){
34730         if(this.collapsed){
34731             return this.collapsedEl.getBox();
34732         }
34733         var box = this.el.getBox();
34734         if(this.split){
34735             box.height += this.split.el.getHeight();
34736         }
34737         return box;
34738     },
34739     
34740     updateBox : function(box){
34741         if(this.split && !this.collapsed){
34742             box.height -= this.split.el.getHeight();
34743             this.split.el.setLeft(box.x);
34744             this.split.el.setTop(box.y+box.height);
34745             this.split.el.setWidth(box.width);
34746         }
34747         if(this.collapsed){
34748             this.updateBody(box.width, null);
34749         }
34750         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34751     }
34752 });
34753
34754 Roo.SouthLayoutRegion = function(mgr, config){
34755     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34756     if(this.split){
34757         this.split.placement = Roo.SplitBar.BOTTOM;
34758         this.split.orientation = Roo.SplitBar.VERTICAL;
34759         this.split.el.addClass("x-layout-split-v");
34760     }
34761     var size = config.initialSize || config.height;
34762     if(typeof size != "undefined"){
34763         this.el.setHeight(size);
34764     }
34765 };
34766 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34767     orientation: Roo.SplitBar.VERTICAL,
34768     getBox : function(){
34769         if(this.collapsed){
34770             return this.collapsedEl.getBox();
34771         }
34772         var box = this.el.getBox();
34773         if(this.split){
34774             var sh = this.split.el.getHeight();
34775             box.height += sh;
34776             box.y -= sh;
34777         }
34778         return box;
34779     },
34780     
34781     updateBox : function(box){
34782         if(this.split && !this.collapsed){
34783             var sh = this.split.el.getHeight();
34784             box.height -= sh;
34785             box.y += sh;
34786             this.split.el.setLeft(box.x);
34787             this.split.el.setTop(box.y-sh);
34788             this.split.el.setWidth(box.width);
34789         }
34790         if(this.collapsed){
34791             this.updateBody(box.width, null);
34792         }
34793         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34794     }
34795 });
34796
34797 Roo.EastLayoutRegion = function(mgr, config){
34798     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34799     if(this.split){
34800         this.split.placement = Roo.SplitBar.RIGHT;
34801         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34802         this.split.el.addClass("x-layout-split-h");
34803     }
34804     var size = config.initialSize || config.width;
34805     if(typeof size != "undefined"){
34806         this.el.setWidth(size);
34807     }
34808 };
34809 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34810     orientation: Roo.SplitBar.HORIZONTAL,
34811     getBox : function(){
34812         if(this.collapsed){
34813             return this.collapsedEl.getBox();
34814         }
34815         var box = this.el.getBox();
34816         if(this.split){
34817             var sw = this.split.el.getWidth();
34818             box.width += sw;
34819             box.x -= sw;
34820         }
34821         return box;
34822     },
34823
34824     updateBox : function(box){
34825         if(this.split && !this.collapsed){
34826             var sw = this.split.el.getWidth();
34827             box.width -= sw;
34828             this.split.el.setLeft(box.x);
34829             this.split.el.setTop(box.y);
34830             this.split.el.setHeight(box.height);
34831             box.x += sw;
34832         }
34833         if(this.collapsed){
34834             this.updateBody(null, box.height);
34835         }
34836         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34837     }
34838 });
34839
34840 Roo.WestLayoutRegion = function(mgr, config){
34841     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34842     if(this.split){
34843         this.split.placement = Roo.SplitBar.LEFT;
34844         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34845         this.split.el.addClass("x-layout-split-h");
34846     }
34847     var size = config.initialSize || config.width;
34848     if(typeof size != "undefined"){
34849         this.el.setWidth(size);
34850     }
34851 };
34852 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34853     orientation: Roo.SplitBar.HORIZONTAL,
34854     getBox : function(){
34855         if(this.collapsed){
34856             return this.collapsedEl.getBox();
34857         }
34858         var box = this.el.getBox();
34859         if(this.split){
34860             box.width += this.split.el.getWidth();
34861         }
34862         return box;
34863     },
34864     
34865     updateBox : function(box){
34866         if(this.split && !this.collapsed){
34867             var sw = this.split.el.getWidth();
34868             box.width -= sw;
34869             this.split.el.setLeft(box.x+box.width);
34870             this.split.el.setTop(box.y);
34871             this.split.el.setHeight(box.height);
34872         }
34873         if(this.collapsed){
34874             this.updateBody(null, box.height);
34875         }
34876         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34877     }
34878 });
34879 /*
34880  * Based on:
34881  * Ext JS Library 1.1.1
34882  * Copyright(c) 2006-2007, Ext JS, LLC.
34883  *
34884  * Originally Released Under LGPL - original licence link has changed is not relivant.
34885  *
34886  * Fork - LGPL
34887  * <script type="text/javascript">
34888  */
34889  
34890  
34891 /*
34892  * Private internal class for reading and applying state
34893  */
34894 Roo.LayoutStateManager = function(layout){
34895      // default empty state
34896      this.state = {
34897         north: {},
34898         south: {},
34899         east: {},
34900         west: {}       
34901     };
34902 };
34903
34904 Roo.LayoutStateManager.prototype = {
34905     init : function(layout, provider){
34906         this.provider = provider;
34907         var state = provider.get(layout.id+"-layout-state");
34908         if(state){
34909             var wasUpdating = layout.isUpdating();
34910             if(!wasUpdating){
34911                 layout.beginUpdate();
34912             }
34913             for(var key in state){
34914                 if(typeof state[key] != "function"){
34915                     var rstate = state[key];
34916                     var r = layout.getRegion(key);
34917                     if(r && rstate){
34918                         if(rstate.size){
34919                             r.resizeTo(rstate.size);
34920                         }
34921                         if(rstate.collapsed == true){
34922                             r.collapse(true);
34923                         }else{
34924                             r.expand(null, true);
34925                         }
34926                     }
34927                 }
34928             }
34929             if(!wasUpdating){
34930                 layout.endUpdate();
34931             }
34932             this.state = state; 
34933         }
34934         this.layout = layout;
34935         layout.on("regionresized", this.onRegionResized, this);
34936         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34937         layout.on("regionexpanded", this.onRegionExpanded, this);
34938     },
34939     
34940     storeState : function(){
34941         this.provider.set(this.layout.id+"-layout-state", this.state);
34942     },
34943     
34944     onRegionResized : function(region, newSize){
34945         this.state[region.getPosition()].size = newSize;
34946         this.storeState();
34947     },
34948     
34949     onRegionCollapsed : function(region){
34950         this.state[region.getPosition()].collapsed = true;
34951         this.storeState();
34952     },
34953     
34954     onRegionExpanded : function(region){
34955         this.state[region.getPosition()].collapsed = false;
34956         this.storeState();
34957     }
34958 };/*
34959  * Based on:
34960  * Ext JS Library 1.1.1
34961  * Copyright(c) 2006-2007, Ext JS, LLC.
34962  *
34963  * Originally Released Under LGPL - original licence link has changed is not relivant.
34964  *
34965  * Fork - LGPL
34966  * <script type="text/javascript">
34967  */
34968 /**
34969  * @class Roo.ContentPanel
34970  * @extends Roo.util.Observable
34971  * A basic ContentPanel element.
34972  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34973  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34974  * @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
34975  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34976  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34977  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34978  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34979  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34980  * @cfg {String} title          The title for this panel
34981  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34982  * @cfg {String} url            Calls {@link #setUrl} with this value
34983  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34984  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34985  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34986  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34987
34988  * @constructor
34989  * Create a new ContentPanel.
34990  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34991  * @param {String/Object} config A string to set only the title or a config object
34992  * @param {String} content (optional) Set the HTML content for this panel
34993  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34994  */
34995 Roo.ContentPanel = function(el, config, content){
34996     
34997      
34998     /*
34999     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35000         config = el;
35001         el = Roo.id();
35002     }
35003     if (config && config.parentLayout) { 
35004         el = config.parentLayout.el.createChild(); 
35005     }
35006     */
35007     if(el.autoCreate){ // xtype is available if this is called from factory
35008         config = el;
35009         el = Roo.id();
35010     }
35011     this.el = Roo.get(el);
35012     if(!this.el && config && config.autoCreate){
35013         if(typeof config.autoCreate == "object"){
35014             if(!config.autoCreate.id){
35015                 config.autoCreate.id = config.id||el;
35016             }
35017             this.el = Roo.DomHelper.append(document.body,
35018                         config.autoCreate, true);
35019         }else{
35020             this.el = Roo.DomHelper.append(document.body,
35021                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35022         }
35023     }
35024     this.closable = false;
35025     this.loaded = false;
35026     this.active = false;
35027     if(typeof config == "string"){
35028         this.title = config;
35029     }else{
35030         Roo.apply(this, config);
35031     }
35032     
35033     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35034         this.wrapEl = this.el.wrap();
35035         this.toolbar.container = this.el.insertSibling(false, 'before');
35036         this.toolbar = new Roo.Toolbar(this.toolbar);
35037     }
35038     
35039     // xtype created footer. - not sure if will work as we normally have to render first..
35040     if (this.footer && !this.footer.el && this.footer.xtype) {
35041         if (!this.wrapEl) {
35042             this.wrapEl = this.el.wrap();
35043         }
35044     
35045         this.footer.container = this.wrapEl.createChild();
35046          
35047         this.footer = Roo.factory(this.footer, Roo);
35048         
35049     }
35050     
35051     if(this.resizeEl){
35052         this.resizeEl = Roo.get(this.resizeEl, true);
35053     }else{
35054         this.resizeEl = this.el;
35055     }
35056     // handle view.xtype
35057     
35058  
35059     
35060     
35061     this.addEvents({
35062         /**
35063          * @event activate
35064          * Fires when this panel is activated. 
35065          * @param {Roo.ContentPanel} this
35066          */
35067         "activate" : true,
35068         /**
35069          * @event deactivate
35070          * Fires when this panel is activated. 
35071          * @param {Roo.ContentPanel} this
35072          */
35073         "deactivate" : true,
35074
35075         /**
35076          * @event resize
35077          * Fires when this panel is resized if fitToFrame is true.
35078          * @param {Roo.ContentPanel} this
35079          * @param {Number} width The width after any component adjustments
35080          * @param {Number} height The height after any component adjustments
35081          */
35082         "resize" : true,
35083         
35084          /**
35085          * @event render
35086          * Fires when this tab is created
35087          * @param {Roo.ContentPanel} this
35088          */
35089         "render" : true
35090         
35091         
35092         
35093     });
35094     
35095
35096     
35097     
35098     if(this.autoScroll){
35099         this.resizeEl.setStyle("overflow", "auto");
35100     } else {
35101         // fix randome scrolling
35102         this.el.on('scroll', function() {
35103             Roo.log('fix random scolling');
35104             this.scrollTo('top',0); 
35105         });
35106     }
35107     content = content || this.content;
35108     if(content){
35109         this.setContent(content);
35110     }
35111     if(config && config.url){
35112         this.setUrl(this.url, this.params, this.loadOnce);
35113     }
35114     
35115     
35116     
35117     Roo.ContentPanel.superclass.constructor.call(this);
35118     
35119     if (this.view && typeof(this.view.xtype) != 'undefined') {
35120         this.view.el = this.el.appendChild(document.createElement("div"));
35121         this.view = Roo.factory(this.view); 
35122         this.view.render  &&  this.view.render(false, '');  
35123     }
35124     
35125     
35126     this.fireEvent('render', this);
35127 };
35128
35129 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35130     tabTip:'',
35131     setRegion : function(region){
35132         this.region = region;
35133         if(region){
35134            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35135         }else{
35136            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35137         } 
35138     },
35139     
35140     /**
35141      * Returns the toolbar for this Panel if one was configured. 
35142      * @return {Roo.Toolbar} 
35143      */
35144     getToolbar : function(){
35145         return this.toolbar;
35146     },
35147     
35148     setActiveState : function(active){
35149         this.active = active;
35150         if(!active){
35151             this.fireEvent("deactivate", this);
35152         }else{
35153             this.fireEvent("activate", this);
35154         }
35155     },
35156     /**
35157      * Updates this panel's element
35158      * @param {String} content The new content
35159      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35160     */
35161     setContent : function(content, loadScripts){
35162         this.el.update(content, loadScripts);
35163     },
35164
35165     ignoreResize : function(w, h){
35166         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35167             return true;
35168         }else{
35169             this.lastSize = {width: w, height: h};
35170             return false;
35171         }
35172     },
35173     /**
35174      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35175      * @return {Roo.UpdateManager} The UpdateManager
35176      */
35177     getUpdateManager : function(){
35178         return this.el.getUpdateManager();
35179     },
35180      /**
35181      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35182      * @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:
35183 <pre><code>
35184 panel.load({
35185     url: "your-url.php",
35186     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35187     callback: yourFunction,
35188     scope: yourObject, //(optional scope)
35189     discardUrl: false,
35190     nocache: false,
35191     text: "Loading...",
35192     timeout: 30,
35193     scripts: false
35194 });
35195 </code></pre>
35196      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35197      * 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.
35198      * @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}
35199      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35200      * @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.
35201      * @return {Roo.ContentPanel} this
35202      */
35203     load : function(){
35204         var um = this.el.getUpdateManager();
35205         um.update.apply(um, arguments);
35206         return this;
35207     },
35208
35209
35210     /**
35211      * 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.
35212      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35213      * @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)
35214      * @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)
35215      * @return {Roo.UpdateManager} The UpdateManager
35216      */
35217     setUrl : function(url, params, loadOnce){
35218         if(this.refreshDelegate){
35219             this.removeListener("activate", this.refreshDelegate);
35220         }
35221         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35222         this.on("activate", this.refreshDelegate);
35223         return this.el.getUpdateManager();
35224     },
35225     
35226     _handleRefresh : function(url, params, loadOnce){
35227         if(!loadOnce || !this.loaded){
35228             var updater = this.el.getUpdateManager();
35229             updater.update(url, params, this._setLoaded.createDelegate(this));
35230         }
35231     },
35232     
35233     _setLoaded : function(){
35234         this.loaded = true;
35235     }, 
35236     
35237     /**
35238      * Returns this panel's id
35239      * @return {String} 
35240      */
35241     getId : function(){
35242         return this.el.id;
35243     },
35244     
35245     /** 
35246      * Returns this panel's element - used by regiosn to add.
35247      * @return {Roo.Element} 
35248      */
35249     getEl : function(){
35250         return this.wrapEl || this.el;
35251     },
35252     
35253     adjustForComponents : function(width, height)
35254     {
35255         //Roo.log('adjustForComponents ');
35256         if(this.resizeEl != this.el){
35257             width -= this.el.getFrameWidth('lr');
35258             height -= this.el.getFrameWidth('tb');
35259         }
35260         if(this.toolbar){
35261             var te = this.toolbar.getEl();
35262             height -= te.getHeight();
35263             te.setWidth(width);
35264         }
35265         if(this.footer){
35266             var te = this.footer.getEl();
35267             Roo.log("footer:" + te.getHeight());
35268             
35269             height -= te.getHeight();
35270             te.setWidth(width);
35271         }
35272         
35273         
35274         if(this.adjustments){
35275             width += this.adjustments[0];
35276             height += this.adjustments[1];
35277         }
35278         return {"width": width, "height": height};
35279     },
35280     
35281     setSize : function(width, height){
35282         if(this.fitToFrame && !this.ignoreResize(width, height)){
35283             if(this.fitContainer && this.resizeEl != this.el){
35284                 this.el.setSize(width, height);
35285             }
35286             var size = this.adjustForComponents(width, height);
35287             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35288             this.fireEvent('resize', this, size.width, size.height);
35289         }
35290     },
35291     
35292     /**
35293      * Returns this panel's title
35294      * @return {String} 
35295      */
35296     getTitle : function(){
35297         return this.title;
35298     },
35299     
35300     /**
35301      * Set this panel's title
35302      * @param {String} title
35303      */
35304     setTitle : function(title){
35305         this.title = title;
35306         if(this.region){
35307             this.region.updatePanelTitle(this, title);
35308         }
35309     },
35310     
35311     /**
35312      * Returns true is this panel was configured to be closable
35313      * @return {Boolean} 
35314      */
35315     isClosable : function(){
35316         return this.closable;
35317     },
35318     
35319     beforeSlide : function(){
35320         this.el.clip();
35321         this.resizeEl.clip();
35322     },
35323     
35324     afterSlide : function(){
35325         this.el.unclip();
35326         this.resizeEl.unclip();
35327     },
35328     
35329     /**
35330      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35331      *   Will fail silently if the {@link #setUrl} method has not been called.
35332      *   This does not activate the panel, just updates its content.
35333      */
35334     refresh : function(){
35335         if(this.refreshDelegate){
35336            this.loaded = false;
35337            this.refreshDelegate();
35338         }
35339     },
35340     
35341     /**
35342      * Destroys this panel
35343      */
35344     destroy : function(){
35345         this.el.removeAllListeners();
35346         var tempEl = document.createElement("span");
35347         tempEl.appendChild(this.el.dom);
35348         tempEl.innerHTML = "";
35349         this.el.remove();
35350         this.el = null;
35351     },
35352     
35353     /**
35354      * form - if the content panel contains a form - this is a reference to it.
35355      * @type {Roo.form.Form}
35356      */
35357     form : false,
35358     /**
35359      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35360      *    This contains a reference to it.
35361      * @type {Roo.View}
35362      */
35363     view : false,
35364     
35365       /**
35366      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35367      * <pre><code>
35368
35369 layout.addxtype({
35370        xtype : 'Form',
35371        items: [ .... ]
35372    }
35373 );
35374
35375 </code></pre>
35376      * @param {Object} cfg Xtype definition of item to add.
35377      */
35378     
35379     addxtype : function(cfg) {
35380         // add form..
35381         if (cfg.xtype.match(/^Form$/)) {
35382             
35383             var el;
35384             //if (this.footer) {
35385             //    el = this.footer.container.insertSibling(false, 'before');
35386             //} else {
35387                 el = this.el.createChild();
35388             //}
35389
35390             this.form = new  Roo.form.Form(cfg);
35391             
35392             
35393             if ( this.form.allItems.length) this.form.render(el.dom);
35394             return this.form;
35395         }
35396         // should only have one of theses..
35397         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35398             // views.. should not be just added - used named prop 'view''
35399             
35400             cfg.el = this.el.appendChild(document.createElement("div"));
35401             // factory?
35402             
35403             var ret = new Roo.factory(cfg);
35404              
35405              ret.render && ret.render(false, ''); // render blank..
35406             this.view = ret;
35407             return ret;
35408         }
35409         return false;
35410     }
35411 });
35412
35413 /**
35414  * @class Roo.GridPanel
35415  * @extends Roo.ContentPanel
35416  * @constructor
35417  * Create a new GridPanel.
35418  * @param {Roo.grid.Grid} grid The grid for this panel
35419  * @param {String/Object} config A string to set only the panel's title, or a config object
35420  */
35421 Roo.GridPanel = function(grid, config){
35422     
35423   
35424     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35425         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35426         
35427     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35428     
35429     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35430     
35431     if(this.toolbar){
35432         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35433     }
35434     // xtype created footer. - not sure if will work as we normally have to render first..
35435     if (this.footer && !this.footer.el && this.footer.xtype) {
35436         
35437         this.footer.container = this.grid.getView().getFooterPanel(true);
35438         this.footer.dataSource = this.grid.dataSource;
35439         this.footer = Roo.factory(this.footer, Roo);
35440         
35441     }
35442     
35443     grid.monitorWindowResize = false; // turn off autosizing
35444     grid.autoHeight = false;
35445     grid.autoWidth = false;
35446     this.grid = grid;
35447     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35448 };
35449
35450 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35451     getId : function(){
35452         return this.grid.id;
35453     },
35454     
35455     /**
35456      * Returns the grid for this panel
35457      * @return {Roo.grid.Grid} 
35458      */
35459     getGrid : function(){
35460         return this.grid;    
35461     },
35462     
35463     setSize : function(width, height){
35464         if(!this.ignoreResize(width, height)){
35465             var grid = this.grid;
35466             var size = this.adjustForComponents(width, height);
35467             grid.getGridEl().setSize(size.width, size.height);
35468             grid.autoSize();
35469         }
35470     },
35471     
35472     beforeSlide : function(){
35473         this.grid.getView().scroller.clip();
35474     },
35475     
35476     afterSlide : function(){
35477         this.grid.getView().scroller.unclip();
35478     },
35479     
35480     destroy : function(){
35481         this.grid.destroy();
35482         delete this.grid;
35483         Roo.GridPanel.superclass.destroy.call(this); 
35484     }
35485 });
35486
35487
35488 /**
35489  * @class Roo.NestedLayoutPanel
35490  * @extends Roo.ContentPanel
35491  * @constructor
35492  * Create a new NestedLayoutPanel.
35493  * 
35494  * 
35495  * @param {Roo.BorderLayout} layout The layout for this panel
35496  * @param {String/Object} config A string to set only the title or a config object
35497  */
35498 Roo.NestedLayoutPanel = function(layout, config)
35499 {
35500     // construct with only one argument..
35501     /* FIXME - implement nicer consturctors
35502     if (layout.layout) {
35503         config = layout;
35504         layout = config.layout;
35505         delete config.layout;
35506     }
35507     if (layout.xtype && !layout.getEl) {
35508         // then layout needs constructing..
35509         layout = Roo.factory(layout, Roo);
35510     }
35511     */
35512     
35513     
35514     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35515     
35516     layout.monitorWindowResize = false; // turn off autosizing
35517     this.layout = layout;
35518     this.layout.getEl().addClass("x-layout-nested-layout");
35519     
35520     
35521     
35522     
35523 };
35524
35525 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35526
35527     setSize : function(width, height){
35528         if(!this.ignoreResize(width, height)){
35529             var size = this.adjustForComponents(width, height);
35530             var el = this.layout.getEl();
35531             el.setSize(size.width, size.height);
35532             var touch = el.dom.offsetWidth;
35533             this.layout.layout();
35534             // ie requires a double layout on the first pass
35535             if(Roo.isIE && !this.initialized){
35536                 this.initialized = true;
35537                 this.layout.layout();
35538             }
35539         }
35540     },
35541     
35542     // activate all subpanels if not currently active..
35543     
35544     setActiveState : function(active){
35545         this.active = active;
35546         if(!active){
35547             this.fireEvent("deactivate", this);
35548             return;
35549         }
35550         
35551         this.fireEvent("activate", this);
35552         // not sure if this should happen before or after..
35553         if (!this.layout) {
35554             return; // should not happen..
35555         }
35556         var reg = false;
35557         for (var r in this.layout.regions) {
35558             reg = this.layout.getRegion(r);
35559             if (reg.getActivePanel()) {
35560                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35561                 reg.setActivePanel(reg.getActivePanel());
35562                 continue;
35563             }
35564             if (!reg.panels.length) {
35565                 continue;
35566             }
35567             reg.showPanel(reg.getPanel(0));
35568         }
35569         
35570         
35571         
35572         
35573     },
35574     
35575     /**
35576      * Returns the nested BorderLayout for this panel
35577      * @return {Roo.BorderLayout} 
35578      */
35579     getLayout : function(){
35580         return this.layout;
35581     },
35582     
35583      /**
35584      * Adds a xtype elements to the layout of the nested panel
35585      * <pre><code>
35586
35587 panel.addxtype({
35588        xtype : 'ContentPanel',
35589        region: 'west',
35590        items: [ .... ]
35591    }
35592 );
35593
35594 panel.addxtype({
35595         xtype : 'NestedLayoutPanel',
35596         region: 'west',
35597         layout: {
35598            center: { },
35599            west: { }   
35600         },
35601         items : [ ... list of content panels or nested layout panels.. ]
35602    }
35603 );
35604 </code></pre>
35605      * @param {Object} cfg Xtype definition of item to add.
35606      */
35607     addxtype : function(cfg) {
35608         return this.layout.addxtype(cfg);
35609     
35610     }
35611 });
35612
35613 Roo.ScrollPanel = function(el, config, content){
35614     config = config || {};
35615     config.fitToFrame = true;
35616     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35617     
35618     this.el.dom.style.overflow = "hidden";
35619     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35620     this.el.removeClass("x-layout-inactive-content");
35621     this.el.on("mousewheel", this.onWheel, this);
35622
35623     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35624     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35625     up.unselectable(); down.unselectable();
35626     up.on("click", this.scrollUp, this);
35627     down.on("click", this.scrollDown, this);
35628     up.addClassOnOver("x-scroller-btn-over");
35629     down.addClassOnOver("x-scroller-btn-over");
35630     up.addClassOnClick("x-scroller-btn-click");
35631     down.addClassOnClick("x-scroller-btn-click");
35632     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35633
35634     this.resizeEl = this.el;
35635     this.el = wrap; this.up = up; this.down = down;
35636 };
35637
35638 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35639     increment : 100,
35640     wheelIncrement : 5,
35641     scrollUp : function(){
35642         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35643     },
35644
35645     scrollDown : function(){
35646         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35647     },
35648
35649     afterScroll : function(){
35650         var el = this.resizeEl;
35651         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35652         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35653         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35654     },
35655
35656     setSize : function(){
35657         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35658         this.afterScroll();
35659     },
35660
35661     onWheel : function(e){
35662         var d = e.getWheelDelta();
35663         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35664         this.afterScroll();
35665         e.stopEvent();
35666     },
35667
35668     setContent : function(content, loadScripts){
35669         this.resizeEl.update(content, loadScripts);
35670     }
35671
35672 });
35673
35674
35675
35676
35677
35678
35679
35680
35681
35682 /**
35683  * @class Roo.TreePanel
35684  * @extends Roo.ContentPanel
35685  * @constructor
35686  * Create a new TreePanel. - defaults to fit/scoll contents.
35687  * @param {String/Object} config A string to set only the panel's title, or a config object
35688  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35689  */
35690 Roo.TreePanel = function(config){
35691     var el = config.el;
35692     var tree = config.tree;
35693     delete config.tree; 
35694     delete config.el; // hopefull!
35695     
35696     // wrapper for IE7 strict & safari scroll issue
35697     
35698     var treeEl = el.createChild();
35699     config.resizeEl = treeEl;
35700     
35701     
35702     
35703     Roo.TreePanel.superclass.constructor.call(this, el, config);
35704  
35705  
35706     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35707     //console.log(tree);
35708     this.on('activate', function()
35709     {
35710         if (this.tree.rendered) {
35711             return;
35712         }
35713         //console.log('render tree');
35714         this.tree.render();
35715     });
35716     // this should not be needed.. - it's actually the 'el' that resizes?
35717     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35718     
35719     //this.on('resize',  function (cp, w, h) {
35720     //        this.tree.innerCt.setWidth(w);
35721     //        this.tree.innerCt.setHeight(h);
35722     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35723     //});
35724
35725         
35726     
35727 };
35728
35729 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35730     fitToFrame : true,
35731     autoScroll : true
35732 });
35733
35734
35735
35736
35737
35738
35739
35740
35741
35742
35743
35744 /*
35745  * Based on:
35746  * Ext JS Library 1.1.1
35747  * Copyright(c) 2006-2007, Ext JS, LLC.
35748  *
35749  * Originally Released Under LGPL - original licence link has changed is not relivant.
35750  *
35751  * Fork - LGPL
35752  * <script type="text/javascript">
35753  */
35754  
35755
35756 /**
35757  * @class Roo.ReaderLayout
35758  * @extends Roo.BorderLayout
35759  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35760  * center region containing two nested regions (a top one for a list view and one for item preview below),
35761  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35762  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35763  * expedites the setup of the overall layout and regions for this common application style.
35764  * Example:
35765  <pre><code>
35766 var reader = new Roo.ReaderLayout();
35767 var CP = Roo.ContentPanel;  // shortcut for adding
35768
35769 reader.beginUpdate();
35770 reader.add("north", new CP("north", "North"));
35771 reader.add("west", new CP("west", {title: "West"}));
35772 reader.add("east", new CP("east", {title: "East"}));
35773
35774 reader.regions.listView.add(new CP("listView", "List"));
35775 reader.regions.preview.add(new CP("preview", "Preview"));
35776 reader.endUpdate();
35777 </code></pre>
35778 * @constructor
35779 * Create a new ReaderLayout
35780 * @param {Object} config Configuration options
35781 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35782 * document.body if omitted)
35783 */
35784 Roo.ReaderLayout = function(config, renderTo){
35785     var c = config || {size:{}};
35786     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35787         north: c.north !== false ? Roo.apply({
35788             split:false,
35789             initialSize: 32,
35790             titlebar: false
35791         }, c.north) : false,
35792         west: c.west !== false ? Roo.apply({
35793             split:true,
35794             initialSize: 200,
35795             minSize: 175,
35796             maxSize: 400,
35797             titlebar: true,
35798             collapsible: true,
35799             animate: true,
35800             margins:{left:5,right:0,bottom:5,top:5},
35801             cmargins:{left:5,right:5,bottom:5,top:5}
35802         }, c.west) : false,
35803         east: c.east !== false ? Roo.apply({
35804             split:true,
35805             initialSize: 200,
35806             minSize: 175,
35807             maxSize: 400,
35808             titlebar: true,
35809             collapsible: true,
35810             animate: true,
35811             margins:{left:0,right:5,bottom:5,top:5},
35812             cmargins:{left:5,right:5,bottom:5,top:5}
35813         }, c.east) : false,
35814         center: Roo.apply({
35815             tabPosition: 'top',
35816             autoScroll:false,
35817             closeOnTab: true,
35818             titlebar:false,
35819             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35820         }, c.center)
35821     });
35822
35823     this.el.addClass('x-reader');
35824
35825     this.beginUpdate();
35826
35827     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35828         south: c.preview !== false ? Roo.apply({
35829             split:true,
35830             initialSize: 200,
35831             minSize: 100,
35832             autoScroll:true,
35833             collapsible:true,
35834             titlebar: true,
35835             cmargins:{top:5,left:0, right:0, bottom:0}
35836         }, c.preview) : false,
35837         center: Roo.apply({
35838             autoScroll:false,
35839             titlebar:false,
35840             minHeight:200
35841         }, c.listView)
35842     });
35843     this.add('center', new Roo.NestedLayoutPanel(inner,
35844             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35845
35846     this.endUpdate();
35847
35848     this.regions.preview = inner.getRegion('south');
35849     this.regions.listView = inner.getRegion('center');
35850 };
35851
35852 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35853  * Based on:
35854  * Ext JS Library 1.1.1
35855  * Copyright(c) 2006-2007, Ext JS, LLC.
35856  *
35857  * Originally Released Under LGPL - original licence link has changed is not relivant.
35858  *
35859  * Fork - LGPL
35860  * <script type="text/javascript">
35861  */
35862  
35863 /**
35864  * @class Roo.grid.Grid
35865  * @extends Roo.util.Observable
35866  * This class represents the primary interface of a component based grid control.
35867  * <br><br>Usage:<pre><code>
35868  var grid = new Roo.grid.Grid("my-container-id", {
35869      ds: myDataStore,
35870      cm: myColModel,
35871      selModel: mySelectionModel,
35872      autoSizeColumns: true,
35873      monitorWindowResize: false,
35874      trackMouseOver: true
35875  });
35876  // set any options
35877  grid.render();
35878  * </code></pre>
35879  * <b>Common Problems:</b><br/>
35880  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35881  * element will correct this<br/>
35882  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35883  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35884  * are unpredictable.<br/>
35885  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35886  * grid to calculate dimensions/offsets.<br/>
35887   * @constructor
35888  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35889  * The container MUST have some type of size defined for the grid to fill. The container will be
35890  * automatically set to position relative if it isn't already.
35891  * @param {Object} config A config object that sets properties on this grid.
35892  */
35893 Roo.grid.Grid = function(container, config){
35894         // initialize the container
35895         this.container = Roo.get(container);
35896         this.container.update("");
35897         this.container.setStyle("overflow", "hidden");
35898     this.container.addClass('x-grid-container');
35899
35900     this.id = this.container.id;
35901
35902     Roo.apply(this, config);
35903     // check and correct shorthanded configs
35904     if(this.ds){
35905         this.dataSource = this.ds;
35906         delete this.ds;
35907     }
35908     if(this.cm){
35909         this.colModel = this.cm;
35910         delete this.cm;
35911     }
35912     if(this.sm){
35913         this.selModel = this.sm;
35914         delete this.sm;
35915     }
35916
35917     if (this.selModel) {
35918         this.selModel = Roo.factory(this.selModel, Roo.grid);
35919         this.sm = this.selModel;
35920         this.sm.xmodule = this.xmodule || false;
35921     }
35922     if (typeof(this.colModel.config) == 'undefined') {
35923         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35924         this.cm = this.colModel;
35925         this.cm.xmodule = this.xmodule || false;
35926     }
35927     if (this.dataSource) {
35928         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35929         this.ds = this.dataSource;
35930         this.ds.xmodule = this.xmodule || false;
35931          
35932     }
35933     
35934     
35935     
35936     if(this.width){
35937         this.container.setWidth(this.width);
35938     }
35939
35940     if(this.height){
35941         this.container.setHeight(this.height);
35942     }
35943     /** @private */
35944         this.addEvents({
35945         // raw events
35946         /**
35947          * @event click
35948          * The raw click event for the entire grid.
35949          * @param {Roo.EventObject} e
35950          */
35951         "click" : true,
35952         /**
35953          * @event dblclick
35954          * The raw dblclick event for the entire grid.
35955          * @param {Roo.EventObject} e
35956          */
35957         "dblclick" : true,
35958         /**
35959          * @event contextmenu
35960          * The raw contextmenu event for the entire grid.
35961          * @param {Roo.EventObject} e
35962          */
35963         "contextmenu" : true,
35964         /**
35965          * @event mousedown
35966          * The raw mousedown event for the entire grid.
35967          * @param {Roo.EventObject} e
35968          */
35969         "mousedown" : true,
35970         /**
35971          * @event mouseup
35972          * The raw mouseup event for the entire grid.
35973          * @param {Roo.EventObject} e
35974          */
35975         "mouseup" : true,
35976         /**
35977          * @event mouseover
35978          * The raw mouseover event for the entire grid.
35979          * @param {Roo.EventObject} e
35980          */
35981         "mouseover" : true,
35982         /**
35983          * @event mouseout
35984          * The raw mouseout event for the entire grid.
35985          * @param {Roo.EventObject} e
35986          */
35987         "mouseout" : true,
35988         /**
35989          * @event keypress
35990          * The raw keypress event for the entire grid.
35991          * @param {Roo.EventObject} e
35992          */
35993         "keypress" : true,
35994         /**
35995          * @event keydown
35996          * The raw keydown event for the entire grid.
35997          * @param {Roo.EventObject} e
35998          */
35999         "keydown" : true,
36000
36001         // custom events
36002
36003         /**
36004          * @event cellclick
36005          * Fires when a cell is clicked
36006          * @param {Grid} this
36007          * @param {Number} rowIndex
36008          * @param {Number} columnIndex
36009          * @param {Roo.EventObject} e
36010          */
36011         "cellclick" : true,
36012         /**
36013          * @event celldblclick
36014          * Fires when a cell is double clicked
36015          * @param {Grid} this
36016          * @param {Number} rowIndex
36017          * @param {Number} columnIndex
36018          * @param {Roo.EventObject} e
36019          */
36020         "celldblclick" : true,
36021         /**
36022          * @event rowclick
36023          * Fires when a row is clicked
36024          * @param {Grid} this
36025          * @param {Number} rowIndex
36026          * @param {Roo.EventObject} e
36027          */
36028         "rowclick" : true,
36029         /**
36030          * @event rowdblclick
36031          * Fires when a row is double clicked
36032          * @param {Grid} this
36033          * @param {Number} rowIndex
36034          * @param {Roo.EventObject} e
36035          */
36036         "rowdblclick" : true,
36037         /**
36038          * @event headerclick
36039          * Fires when a header is clicked
36040          * @param {Grid} this
36041          * @param {Number} columnIndex
36042          * @param {Roo.EventObject} e
36043          */
36044         "headerclick" : true,
36045         /**
36046          * @event headerdblclick
36047          * Fires when a header cell is double clicked
36048          * @param {Grid} this
36049          * @param {Number} columnIndex
36050          * @param {Roo.EventObject} e
36051          */
36052         "headerdblclick" : true,
36053         /**
36054          * @event rowcontextmenu
36055          * Fires when a row is right clicked
36056          * @param {Grid} this
36057          * @param {Number} rowIndex
36058          * @param {Roo.EventObject} e
36059          */
36060         "rowcontextmenu" : true,
36061         /**
36062          * @event cellcontextmenu
36063          * Fires when a cell is right clicked
36064          * @param {Grid} this
36065          * @param {Number} rowIndex
36066          * @param {Number} cellIndex
36067          * @param {Roo.EventObject} e
36068          */
36069          "cellcontextmenu" : true,
36070         /**
36071          * @event headercontextmenu
36072          * Fires when a header is right clicked
36073          * @param {Grid} this
36074          * @param {Number} columnIndex
36075          * @param {Roo.EventObject} e
36076          */
36077         "headercontextmenu" : true,
36078         /**
36079          * @event bodyscroll
36080          * Fires when the body element is scrolled
36081          * @param {Number} scrollLeft
36082          * @param {Number} scrollTop
36083          */
36084         "bodyscroll" : true,
36085         /**
36086          * @event columnresize
36087          * Fires when the user resizes a column
36088          * @param {Number} columnIndex
36089          * @param {Number} newSize
36090          */
36091         "columnresize" : true,
36092         /**
36093          * @event columnmove
36094          * Fires when the user moves a column
36095          * @param {Number} oldIndex
36096          * @param {Number} newIndex
36097          */
36098         "columnmove" : true,
36099         /**
36100          * @event startdrag
36101          * Fires when row(s) start being dragged
36102          * @param {Grid} this
36103          * @param {Roo.GridDD} dd The drag drop object
36104          * @param {event} e The raw browser event
36105          */
36106         "startdrag" : true,
36107         /**
36108          * @event enddrag
36109          * Fires when a drag operation is complete
36110          * @param {Grid} this
36111          * @param {Roo.GridDD} dd The drag drop object
36112          * @param {event} e The raw browser event
36113          */
36114         "enddrag" : true,
36115         /**
36116          * @event dragdrop
36117          * Fires when dragged row(s) are dropped on a valid DD target
36118          * @param {Grid} this
36119          * @param {Roo.GridDD} dd The drag drop object
36120          * @param {String} targetId The target drag drop object
36121          * @param {event} e The raw browser event
36122          */
36123         "dragdrop" : true,
36124         /**
36125          * @event dragover
36126          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36127          * @param {Grid} this
36128          * @param {Roo.GridDD} dd The drag drop object
36129          * @param {String} targetId The target drag drop object
36130          * @param {event} e The raw browser event
36131          */
36132         "dragover" : true,
36133         /**
36134          * @event dragenter
36135          *  Fires when the dragged row(s) first cross another DD target while being dragged
36136          * @param {Grid} this
36137          * @param {Roo.GridDD} dd The drag drop object
36138          * @param {String} targetId The target drag drop object
36139          * @param {event} e The raw browser event
36140          */
36141         "dragenter" : true,
36142         /**
36143          * @event dragout
36144          * Fires when the dragged row(s) leave another DD target while being dragged
36145          * @param {Grid} this
36146          * @param {Roo.GridDD} dd The drag drop object
36147          * @param {String} targetId The target drag drop object
36148          * @param {event} e The raw browser event
36149          */
36150         "dragout" : true,
36151         /**
36152          * @event rowclass
36153          * Fires when a row is rendered, so you can change add a style to it.
36154          * @param {GridView} gridview   The grid view
36155          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36156          */
36157         'rowclass' : true,
36158
36159         /**
36160          * @event render
36161          * Fires when the grid is rendered
36162          * @param {Grid} grid
36163          */
36164         'render' : true
36165     });
36166
36167     Roo.grid.Grid.superclass.constructor.call(this);
36168 };
36169 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36170     
36171     /**
36172      * @cfg {String} ddGroup - drag drop group.
36173      */
36174
36175     /**
36176      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36177      */
36178     minColumnWidth : 25,
36179
36180     /**
36181      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36182      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36183      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36184      */
36185     autoSizeColumns : false,
36186
36187     /**
36188      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36189      */
36190     autoSizeHeaders : true,
36191
36192     /**
36193      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36194      */
36195     monitorWindowResize : true,
36196
36197     /**
36198      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36199      * rows measured to get a columns size. Default is 0 (all rows).
36200      */
36201     maxRowsToMeasure : 0,
36202
36203     /**
36204      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36205      */
36206     trackMouseOver : true,
36207
36208     /**
36209     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36210     */
36211     
36212     /**
36213     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36214     */
36215     enableDragDrop : false,
36216     
36217     /**
36218     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36219     */
36220     enableColumnMove : true,
36221     
36222     /**
36223     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36224     */
36225     enableColumnHide : true,
36226     
36227     /**
36228     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36229     */
36230     enableRowHeightSync : false,
36231     
36232     /**
36233     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36234     */
36235     stripeRows : true,
36236     
36237     /**
36238     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36239     */
36240     autoHeight : false,
36241
36242     /**
36243      * @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.
36244      */
36245     autoExpandColumn : false,
36246
36247     /**
36248     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36249     * Default is 50.
36250     */
36251     autoExpandMin : 50,
36252
36253     /**
36254     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36255     */
36256     autoExpandMax : 1000,
36257
36258     /**
36259     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36260     */
36261     view : null,
36262
36263     /**
36264     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36265     */
36266     loadMask : false,
36267     /**
36268     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36269     */
36270     dropTarget: false,
36271     
36272    
36273     
36274     // private
36275     rendered : false,
36276
36277     /**
36278     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36279     * of a fixed width. Default is false.
36280     */
36281     /**
36282     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36283     */
36284     /**
36285      * Called once after all setup has been completed and the grid is ready to be rendered.
36286      * @return {Roo.grid.Grid} this
36287      */
36288     render : function()
36289     {
36290         var c = this.container;
36291         // try to detect autoHeight/width mode
36292         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36293             this.autoHeight = true;
36294         }
36295         var view = this.getView();
36296         view.init(this);
36297
36298         c.on("click", this.onClick, this);
36299         c.on("dblclick", this.onDblClick, this);
36300         c.on("contextmenu", this.onContextMenu, this);
36301         c.on("keydown", this.onKeyDown, this);
36302         if (Roo.isTouch) {
36303             c.on("touchstart", this.onTouchStart, this);
36304         }
36305
36306         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36307
36308         this.getSelectionModel().init(this);
36309
36310         view.render();
36311
36312         if(this.loadMask){
36313             this.loadMask = new Roo.LoadMask(this.container,
36314                     Roo.apply({store:this.dataSource}, this.loadMask));
36315         }
36316         
36317         
36318         if (this.toolbar && this.toolbar.xtype) {
36319             this.toolbar.container = this.getView().getHeaderPanel(true);
36320             this.toolbar = new Roo.Toolbar(this.toolbar);
36321         }
36322         if (this.footer && this.footer.xtype) {
36323             this.footer.dataSource = this.getDataSource();
36324             this.footer.container = this.getView().getFooterPanel(true);
36325             this.footer = Roo.factory(this.footer, Roo);
36326         }
36327         if (this.dropTarget && this.dropTarget.xtype) {
36328             delete this.dropTarget.xtype;
36329             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36330         }
36331         
36332         
36333         this.rendered = true;
36334         this.fireEvent('render', this);
36335         return this;
36336     },
36337
36338         /**
36339          * Reconfigures the grid to use a different Store and Column Model.
36340          * The View will be bound to the new objects and refreshed.
36341          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36342          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36343          */
36344     reconfigure : function(dataSource, colModel){
36345         if(this.loadMask){
36346             this.loadMask.destroy();
36347             this.loadMask = new Roo.LoadMask(this.container,
36348                     Roo.apply({store:dataSource}, this.loadMask));
36349         }
36350         this.view.bind(dataSource, colModel);
36351         this.dataSource = dataSource;
36352         this.colModel = colModel;
36353         this.view.refresh(true);
36354     },
36355
36356     // private
36357     onKeyDown : function(e){
36358         this.fireEvent("keydown", e);
36359     },
36360
36361     /**
36362      * Destroy this grid.
36363      * @param {Boolean} removeEl True to remove the element
36364      */
36365     destroy : function(removeEl, keepListeners){
36366         if(this.loadMask){
36367             this.loadMask.destroy();
36368         }
36369         var c = this.container;
36370         c.removeAllListeners();
36371         this.view.destroy();
36372         this.colModel.purgeListeners();
36373         if(!keepListeners){
36374             this.purgeListeners();
36375         }
36376         c.update("");
36377         if(removeEl === true){
36378             c.remove();
36379         }
36380     },
36381
36382     // private
36383     processEvent : function(name, e){
36384         // does this fire select???
36385         //Roo.log('grid:processEvent '  + name);
36386         
36387         if (name != 'touchstart' ) {
36388             this.fireEvent(name, e);    
36389         }
36390         
36391         var t = e.getTarget();
36392         var v = this.view;
36393         var header = v.findHeaderIndex(t);
36394         if(header !== false){
36395             var ename = name == 'touchstart' ? 'click' : name;
36396              
36397             this.fireEvent("header" + ename, this, header, e);
36398         }else{
36399             var row = v.findRowIndex(t);
36400             var cell = v.findCellIndex(t);
36401             if (name == 'touchstart') {
36402                 // first touch is always a click.
36403                 // hopefull this happens after selection is updated.?
36404                 name = false;
36405                 
36406                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36407                     var cs = this.selModel.getSelectedCell();
36408                     if (row == cs[0] && cell == cs[1]){
36409                         name = 'dblclick';
36410                     }
36411                 }
36412                 if (typeof(this.selModel.getSelections) != 'undefined') {
36413                     var cs = this.selModel.getSelections();
36414                     var ds = this.dataSource;
36415                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36416                         name = 'dblclick';
36417                     }
36418                 }
36419                 if (!name) {
36420                     return;
36421                 }
36422             }
36423             
36424             
36425             if(row !== false){
36426                 this.fireEvent("row" + name, this, row, e);
36427                 if(cell !== false){
36428                     this.fireEvent("cell" + name, this, row, cell, e);
36429                 }
36430             }
36431         }
36432     },
36433
36434     // private
36435     onClick : function(e){
36436         this.processEvent("click", e);
36437     },
36438    // private
36439     onTouchStart : function(e){
36440         this.processEvent("touchstart", e);
36441     },
36442
36443     // private
36444     onContextMenu : function(e, t){
36445         this.processEvent("contextmenu", e);
36446     },
36447
36448     // private
36449     onDblClick : function(e){
36450         this.processEvent("dblclick", e);
36451     },
36452
36453     // private
36454     walkCells : function(row, col, step, fn, scope){
36455         var cm = this.colModel, clen = cm.getColumnCount();
36456         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36457         if(step < 0){
36458             if(col < 0){
36459                 row--;
36460                 first = false;
36461             }
36462             while(row >= 0){
36463                 if(!first){
36464                     col = clen-1;
36465                 }
36466                 first = false;
36467                 while(col >= 0){
36468                     if(fn.call(scope || this, row, col, cm) === true){
36469                         return [row, col];
36470                     }
36471                     col--;
36472                 }
36473                 row--;
36474             }
36475         } else {
36476             if(col >= clen){
36477                 row++;
36478                 first = false;
36479             }
36480             while(row < rlen){
36481                 if(!first){
36482                     col = 0;
36483                 }
36484                 first = false;
36485                 while(col < clen){
36486                     if(fn.call(scope || this, row, col, cm) === true){
36487                         return [row, col];
36488                     }
36489                     col++;
36490                 }
36491                 row++;
36492             }
36493         }
36494         return null;
36495     },
36496
36497     // private
36498     getSelections : function(){
36499         return this.selModel.getSelections();
36500     },
36501
36502     /**
36503      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36504      * but if manual update is required this method will initiate it.
36505      */
36506     autoSize : function(){
36507         if(this.rendered){
36508             this.view.layout();
36509             if(this.view.adjustForScroll){
36510                 this.view.adjustForScroll();
36511             }
36512         }
36513     },
36514
36515     /**
36516      * Returns the grid's underlying element.
36517      * @return {Element} The element
36518      */
36519     getGridEl : function(){
36520         return this.container;
36521     },
36522
36523     // private for compatibility, overridden by editor grid
36524     stopEditing : function(){},
36525
36526     /**
36527      * Returns the grid's SelectionModel.
36528      * @return {SelectionModel}
36529      */
36530     getSelectionModel : function(){
36531         if(!this.selModel){
36532             this.selModel = new Roo.grid.RowSelectionModel();
36533         }
36534         return this.selModel;
36535     },
36536
36537     /**
36538      * Returns the grid's DataSource.
36539      * @return {DataSource}
36540      */
36541     getDataSource : function(){
36542         return this.dataSource;
36543     },
36544
36545     /**
36546      * Returns the grid's ColumnModel.
36547      * @return {ColumnModel}
36548      */
36549     getColumnModel : function(){
36550         return this.colModel;
36551     },
36552
36553     /**
36554      * Returns the grid's GridView object.
36555      * @return {GridView}
36556      */
36557     getView : function(){
36558         if(!this.view){
36559             this.view = new Roo.grid.GridView(this.viewConfig);
36560         }
36561         return this.view;
36562     },
36563     /**
36564      * Called to get grid's drag proxy text, by default returns this.ddText.
36565      * @return {String}
36566      */
36567     getDragDropText : function(){
36568         var count = this.selModel.getCount();
36569         return String.format(this.ddText, count, count == 1 ? '' : 's');
36570     }
36571 });
36572 /**
36573  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36574  * %0 is replaced with the number of selected rows.
36575  * @type String
36576  */
36577 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36578  * Based on:
36579  * Ext JS Library 1.1.1
36580  * Copyright(c) 2006-2007, Ext JS, LLC.
36581  *
36582  * Originally Released Under LGPL - original licence link has changed is not relivant.
36583  *
36584  * Fork - LGPL
36585  * <script type="text/javascript">
36586  */
36587  
36588 Roo.grid.AbstractGridView = function(){
36589         this.grid = null;
36590         
36591         this.events = {
36592             "beforerowremoved" : true,
36593             "beforerowsinserted" : true,
36594             "beforerefresh" : true,
36595             "rowremoved" : true,
36596             "rowsinserted" : true,
36597             "rowupdated" : true,
36598             "refresh" : true
36599         };
36600     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36601 };
36602
36603 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36604     rowClass : "x-grid-row",
36605     cellClass : "x-grid-cell",
36606     tdClass : "x-grid-td",
36607     hdClass : "x-grid-hd",
36608     splitClass : "x-grid-hd-split",
36609     
36610     init: function(grid){
36611         this.grid = grid;
36612                 var cid = this.grid.getGridEl().id;
36613         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36614         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36615         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36616         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36617         },
36618         
36619     getColumnRenderers : function(){
36620         var renderers = [];
36621         var cm = this.grid.colModel;
36622         var colCount = cm.getColumnCount();
36623         for(var i = 0; i < colCount; i++){
36624             renderers[i] = cm.getRenderer(i);
36625         }
36626         return renderers;
36627     },
36628     
36629     getColumnIds : function(){
36630         var ids = [];
36631         var cm = this.grid.colModel;
36632         var colCount = cm.getColumnCount();
36633         for(var i = 0; i < colCount; i++){
36634             ids[i] = cm.getColumnId(i);
36635         }
36636         return ids;
36637     },
36638     
36639     getDataIndexes : function(){
36640         if(!this.indexMap){
36641             this.indexMap = this.buildIndexMap();
36642         }
36643         return this.indexMap.colToData;
36644     },
36645     
36646     getColumnIndexByDataIndex : function(dataIndex){
36647         if(!this.indexMap){
36648             this.indexMap = this.buildIndexMap();
36649         }
36650         return this.indexMap.dataToCol[dataIndex];
36651     },
36652     
36653     /**
36654      * Set a css style for a column dynamically. 
36655      * @param {Number} colIndex The index of the column
36656      * @param {String} name The css property name
36657      * @param {String} value The css value
36658      */
36659     setCSSStyle : function(colIndex, name, value){
36660         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36661         Roo.util.CSS.updateRule(selector, name, value);
36662     },
36663     
36664     generateRules : function(cm){
36665         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36666         Roo.util.CSS.removeStyleSheet(rulesId);
36667         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36668             var cid = cm.getColumnId(i);
36669             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36670                          this.tdSelector, cid, " {\n}\n",
36671                          this.hdSelector, cid, " {\n}\n",
36672                          this.splitSelector, cid, " {\n}\n");
36673         }
36674         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36675     }
36676 });/*
36677  * Based on:
36678  * Ext JS Library 1.1.1
36679  * Copyright(c) 2006-2007, Ext JS, LLC.
36680  *
36681  * Originally Released Under LGPL - original licence link has changed is not relivant.
36682  *
36683  * Fork - LGPL
36684  * <script type="text/javascript">
36685  */
36686
36687 // private
36688 // This is a support class used internally by the Grid components
36689 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36690     this.grid = grid;
36691     this.view = grid.getView();
36692     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36693     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36694     if(hd2){
36695         this.setHandleElId(Roo.id(hd));
36696         this.setOuterHandleElId(Roo.id(hd2));
36697     }
36698     this.scroll = false;
36699 };
36700 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36701     maxDragWidth: 120,
36702     getDragData : function(e){
36703         var t = Roo.lib.Event.getTarget(e);
36704         var h = this.view.findHeaderCell(t);
36705         if(h){
36706             return {ddel: h.firstChild, header:h};
36707         }
36708         return false;
36709     },
36710
36711     onInitDrag : function(e){
36712         this.view.headersDisabled = true;
36713         var clone = this.dragData.ddel.cloneNode(true);
36714         clone.id = Roo.id();
36715         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36716         this.proxy.update(clone);
36717         return true;
36718     },
36719
36720     afterValidDrop : function(){
36721         var v = this.view;
36722         setTimeout(function(){
36723             v.headersDisabled = false;
36724         }, 50);
36725     },
36726
36727     afterInvalidDrop : function(){
36728         var v = this.view;
36729         setTimeout(function(){
36730             v.headersDisabled = false;
36731         }, 50);
36732     }
36733 });
36734 /*
36735  * Based on:
36736  * Ext JS Library 1.1.1
36737  * Copyright(c) 2006-2007, Ext JS, LLC.
36738  *
36739  * Originally Released Under LGPL - original licence link has changed is not relivant.
36740  *
36741  * Fork - LGPL
36742  * <script type="text/javascript">
36743  */
36744 // private
36745 // This is a support class used internally by the Grid components
36746 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36747     this.grid = grid;
36748     this.view = grid.getView();
36749     // split the proxies so they don't interfere with mouse events
36750     this.proxyTop = Roo.DomHelper.append(document.body, {
36751         cls:"col-move-top", html:"&#160;"
36752     }, true);
36753     this.proxyBottom = Roo.DomHelper.append(document.body, {
36754         cls:"col-move-bottom", html:"&#160;"
36755     }, true);
36756     this.proxyTop.hide = this.proxyBottom.hide = function(){
36757         this.setLeftTop(-100,-100);
36758         this.setStyle("visibility", "hidden");
36759     };
36760     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36761     // temporarily disabled
36762     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36763     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36764 };
36765 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36766     proxyOffsets : [-4, -9],
36767     fly: Roo.Element.fly,
36768
36769     getTargetFromEvent : function(e){
36770         var t = Roo.lib.Event.getTarget(e);
36771         var cindex = this.view.findCellIndex(t);
36772         if(cindex !== false){
36773             return this.view.getHeaderCell(cindex);
36774         }
36775         return null;
36776     },
36777
36778     nextVisible : function(h){
36779         var v = this.view, cm = this.grid.colModel;
36780         h = h.nextSibling;
36781         while(h){
36782             if(!cm.isHidden(v.getCellIndex(h))){
36783                 return h;
36784             }
36785             h = h.nextSibling;
36786         }
36787         return null;
36788     },
36789
36790     prevVisible : function(h){
36791         var v = this.view, cm = this.grid.colModel;
36792         h = h.prevSibling;
36793         while(h){
36794             if(!cm.isHidden(v.getCellIndex(h))){
36795                 return h;
36796             }
36797             h = h.prevSibling;
36798         }
36799         return null;
36800     },
36801
36802     positionIndicator : function(h, n, e){
36803         var x = Roo.lib.Event.getPageX(e);
36804         var r = Roo.lib.Dom.getRegion(n.firstChild);
36805         var px, pt, py = r.top + this.proxyOffsets[1];
36806         if((r.right - x) <= (r.right-r.left)/2){
36807             px = r.right+this.view.borderWidth;
36808             pt = "after";
36809         }else{
36810             px = r.left;
36811             pt = "before";
36812         }
36813         var oldIndex = this.view.getCellIndex(h);
36814         var newIndex = this.view.getCellIndex(n);
36815
36816         if(this.grid.colModel.isFixed(newIndex)){
36817             return false;
36818         }
36819
36820         var locked = this.grid.colModel.isLocked(newIndex);
36821
36822         if(pt == "after"){
36823             newIndex++;
36824         }
36825         if(oldIndex < newIndex){
36826             newIndex--;
36827         }
36828         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36829             return false;
36830         }
36831         px +=  this.proxyOffsets[0];
36832         this.proxyTop.setLeftTop(px, py);
36833         this.proxyTop.show();
36834         if(!this.bottomOffset){
36835             this.bottomOffset = this.view.mainHd.getHeight();
36836         }
36837         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36838         this.proxyBottom.show();
36839         return pt;
36840     },
36841
36842     onNodeEnter : function(n, dd, e, data){
36843         if(data.header != n){
36844             this.positionIndicator(data.header, n, e);
36845         }
36846     },
36847
36848     onNodeOver : function(n, dd, e, data){
36849         var result = false;
36850         if(data.header != n){
36851             result = this.positionIndicator(data.header, n, e);
36852         }
36853         if(!result){
36854             this.proxyTop.hide();
36855             this.proxyBottom.hide();
36856         }
36857         return result ? this.dropAllowed : this.dropNotAllowed;
36858     },
36859
36860     onNodeOut : function(n, dd, e, data){
36861         this.proxyTop.hide();
36862         this.proxyBottom.hide();
36863     },
36864
36865     onNodeDrop : function(n, dd, e, data){
36866         var h = data.header;
36867         if(h != n){
36868             var cm = this.grid.colModel;
36869             var x = Roo.lib.Event.getPageX(e);
36870             var r = Roo.lib.Dom.getRegion(n.firstChild);
36871             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36872             var oldIndex = this.view.getCellIndex(h);
36873             var newIndex = this.view.getCellIndex(n);
36874             var locked = cm.isLocked(newIndex);
36875             if(pt == "after"){
36876                 newIndex++;
36877             }
36878             if(oldIndex < newIndex){
36879                 newIndex--;
36880             }
36881             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36882                 return false;
36883             }
36884             cm.setLocked(oldIndex, locked, true);
36885             cm.moveColumn(oldIndex, newIndex);
36886             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36887             return true;
36888         }
36889         return false;
36890     }
36891 });
36892 /*
36893  * Based on:
36894  * Ext JS Library 1.1.1
36895  * Copyright(c) 2006-2007, Ext JS, LLC.
36896  *
36897  * Originally Released Under LGPL - original licence link has changed is not relivant.
36898  *
36899  * Fork - LGPL
36900  * <script type="text/javascript">
36901  */
36902   
36903 /**
36904  * @class Roo.grid.GridView
36905  * @extends Roo.util.Observable
36906  *
36907  * @constructor
36908  * @param {Object} config
36909  */
36910 Roo.grid.GridView = function(config){
36911     Roo.grid.GridView.superclass.constructor.call(this);
36912     this.el = null;
36913
36914     Roo.apply(this, config);
36915 };
36916
36917 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36918
36919     unselectable :  'unselectable="on"',
36920     unselectableCls :  'x-unselectable',
36921     
36922     
36923     rowClass : "x-grid-row",
36924
36925     cellClass : "x-grid-col",
36926
36927     tdClass : "x-grid-td",
36928
36929     hdClass : "x-grid-hd",
36930
36931     splitClass : "x-grid-split",
36932
36933     sortClasses : ["sort-asc", "sort-desc"],
36934
36935     enableMoveAnim : false,
36936
36937     hlColor: "C3DAF9",
36938
36939     dh : Roo.DomHelper,
36940
36941     fly : Roo.Element.fly,
36942
36943     css : Roo.util.CSS,
36944
36945     borderWidth: 1,
36946
36947     splitOffset: 3,
36948
36949     scrollIncrement : 22,
36950
36951     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36952
36953     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36954
36955     bind : function(ds, cm){
36956         if(this.ds){
36957             this.ds.un("load", this.onLoad, this);
36958             this.ds.un("datachanged", this.onDataChange, this);
36959             this.ds.un("add", this.onAdd, this);
36960             this.ds.un("remove", this.onRemove, this);
36961             this.ds.un("update", this.onUpdate, this);
36962             this.ds.un("clear", this.onClear, this);
36963         }
36964         if(ds){
36965             ds.on("load", this.onLoad, this);
36966             ds.on("datachanged", this.onDataChange, this);
36967             ds.on("add", this.onAdd, this);
36968             ds.on("remove", this.onRemove, this);
36969             ds.on("update", this.onUpdate, this);
36970             ds.on("clear", this.onClear, this);
36971         }
36972         this.ds = ds;
36973
36974         if(this.cm){
36975             this.cm.un("widthchange", this.onColWidthChange, this);
36976             this.cm.un("headerchange", this.onHeaderChange, this);
36977             this.cm.un("hiddenchange", this.onHiddenChange, this);
36978             this.cm.un("columnmoved", this.onColumnMove, this);
36979             this.cm.un("columnlockchange", this.onColumnLock, this);
36980         }
36981         if(cm){
36982             this.generateRules(cm);
36983             cm.on("widthchange", this.onColWidthChange, this);
36984             cm.on("headerchange", this.onHeaderChange, this);
36985             cm.on("hiddenchange", this.onHiddenChange, this);
36986             cm.on("columnmoved", this.onColumnMove, this);
36987             cm.on("columnlockchange", this.onColumnLock, this);
36988         }
36989         this.cm = cm;
36990     },
36991
36992     init: function(grid){
36993         Roo.grid.GridView.superclass.init.call(this, grid);
36994
36995         this.bind(grid.dataSource, grid.colModel);
36996
36997         grid.on("headerclick", this.handleHeaderClick, this);
36998
36999         if(grid.trackMouseOver){
37000             grid.on("mouseover", this.onRowOver, this);
37001             grid.on("mouseout", this.onRowOut, this);
37002         }
37003         grid.cancelTextSelection = function(){};
37004         this.gridId = grid.id;
37005
37006         var tpls = this.templates || {};
37007
37008         if(!tpls.master){
37009             tpls.master = new Roo.Template(
37010                '<div class="x-grid" hidefocus="true">',
37011                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37012                   '<div class="x-grid-topbar"></div>',
37013                   '<div class="x-grid-scroller"><div></div></div>',
37014                   '<div class="x-grid-locked">',
37015                       '<div class="x-grid-header">{lockedHeader}</div>',
37016                       '<div class="x-grid-body">{lockedBody}</div>',
37017                   "</div>",
37018                   '<div class="x-grid-viewport">',
37019                       '<div class="x-grid-header">{header}</div>',
37020                       '<div class="x-grid-body">{body}</div>',
37021                   "</div>",
37022                   '<div class="x-grid-bottombar"></div>',
37023                  
37024                   '<div class="x-grid-resize-proxy">&#160;</div>',
37025                "</div>"
37026             );
37027             tpls.master.disableformats = true;
37028         }
37029
37030         if(!tpls.header){
37031             tpls.header = new Roo.Template(
37032                '<table border="0" cellspacing="0" cellpadding="0">',
37033                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37034                "</table>{splits}"
37035             );
37036             tpls.header.disableformats = true;
37037         }
37038         tpls.header.compile();
37039
37040         if(!tpls.hcell){
37041             tpls.hcell = new Roo.Template(
37042                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37043                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37044                 "</div></td>"
37045              );
37046              tpls.hcell.disableFormats = true;
37047         }
37048         tpls.hcell.compile();
37049
37050         if(!tpls.hsplit){
37051             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37052                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37053             tpls.hsplit.disableFormats = true;
37054         }
37055         tpls.hsplit.compile();
37056
37057         if(!tpls.body){
37058             tpls.body = new Roo.Template(
37059                '<table border="0" cellspacing="0" cellpadding="0">',
37060                "<tbody>{rows}</tbody>",
37061                "</table>"
37062             );
37063             tpls.body.disableFormats = true;
37064         }
37065         tpls.body.compile();
37066
37067         if(!tpls.row){
37068             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37069             tpls.row.disableFormats = true;
37070         }
37071         tpls.row.compile();
37072
37073         if(!tpls.cell){
37074             tpls.cell = new Roo.Template(
37075                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37076                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37077                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37078                 "</td>"
37079             );
37080             tpls.cell.disableFormats = true;
37081         }
37082         tpls.cell.compile();
37083
37084         this.templates = tpls;
37085     },
37086
37087     // remap these for backwards compat
37088     onColWidthChange : function(){
37089         this.updateColumns.apply(this, arguments);
37090     },
37091     onHeaderChange : function(){
37092         this.updateHeaders.apply(this, arguments);
37093     }, 
37094     onHiddenChange : function(){
37095         this.handleHiddenChange.apply(this, arguments);
37096     },
37097     onColumnMove : function(){
37098         this.handleColumnMove.apply(this, arguments);
37099     },
37100     onColumnLock : function(){
37101         this.handleLockChange.apply(this, arguments);
37102     },
37103
37104     onDataChange : function(){
37105         this.refresh();
37106         this.updateHeaderSortState();
37107     },
37108
37109     onClear : function(){
37110         this.refresh();
37111     },
37112
37113     onUpdate : function(ds, record){
37114         this.refreshRow(record);
37115     },
37116
37117     refreshRow : function(record){
37118         var ds = this.ds, index;
37119         if(typeof record == 'number'){
37120             index = record;
37121             record = ds.getAt(index);
37122         }else{
37123             index = ds.indexOf(record);
37124         }
37125         this.insertRows(ds, index, index, true);
37126         this.onRemove(ds, record, index+1, true);
37127         this.syncRowHeights(index, index);
37128         this.layout();
37129         this.fireEvent("rowupdated", this, index, record);
37130     },
37131
37132     onAdd : function(ds, records, index){
37133         this.insertRows(ds, index, index + (records.length-1));
37134     },
37135
37136     onRemove : function(ds, record, index, isUpdate){
37137         if(isUpdate !== true){
37138             this.fireEvent("beforerowremoved", this, index, record);
37139         }
37140         var bt = this.getBodyTable(), lt = this.getLockedTable();
37141         if(bt.rows[index]){
37142             bt.firstChild.removeChild(bt.rows[index]);
37143         }
37144         if(lt.rows[index]){
37145             lt.firstChild.removeChild(lt.rows[index]);
37146         }
37147         if(isUpdate !== true){
37148             this.stripeRows(index);
37149             this.syncRowHeights(index, index);
37150             this.layout();
37151             this.fireEvent("rowremoved", this, index, record);
37152         }
37153     },
37154
37155     onLoad : function(){
37156         this.scrollToTop();
37157     },
37158
37159     /**
37160      * Scrolls the grid to the top
37161      */
37162     scrollToTop : function(){
37163         if(this.scroller){
37164             this.scroller.dom.scrollTop = 0;
37165             this.syncScroll();
37166         }
37167     },
37168
37169     /**
37170      * Gets a panel in the header of the grid that can be used for toolbars etc.
37171      * After modifying the contents of this panel a call to grid.autoSize() may be
37172      * required to register any changes in size.
37173      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37174      * @return Roo.Element
37175      */
37176     getHeaderPanel : function(doShow){
37177         if(doShow){
37178             this.headerPanel.show();
37179         }
37180         return this.headerPanel;
37181     },
37182
37183     /**
37184      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37185      * After modifying the contents of this panel a call to grid.autoSize() may be
37186      * required to register any changes in size.
37187      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37188      * @return Roo.Element
37189      */
37190     getFooterPanel : function(doShow){
37191         if(doShow){
37192             this.footerPanel.show();
37193         }
37194         return this.footerPanel;
37195     },
37196
37197     initElements : function(){
37198         var E = Roo.Element;
37199         var el = this.grid.getGridEl().dom.firstChild;
37200         var cs = el.childNodes;
37201
37202         this.el = new E(el);
37203         
37204          this.focusEl = new E(el.firstChild);
37205         this.focusEl.swallowEvent("click", true);
37206         
37207         this.headerPanel = new E(cs[1]);
37208         this.headerPanel.enableDisplayMode("block");
37209
37210         this.scroller = new E(cs[2]);
37211         this.scrollSizer = new E(this.scroller.dom.firstChild);
37212
37213         this.lockedWrap = new E(cs[3]);
37214         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37215         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37216
37217         this.mainWrap = new E(cs[4]);
37218         this.mainHd = new E(this.mainWrap.dom.firstChild);
37219         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37220
37221         this.footerPanel = new E(cs[5]);
37222         this.footerPanel.enableDisplayMode("block");
37223
37224         this.resizeProxy = new E(cs[6]);
37225
37226         this.headerSelector = String.format(
37227            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37228            this.lockedHd.id, this.mainHd.id
37229         );
37230
37231         this.splitterSelector = String.format(
37232            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37233            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37234         );
37235     },
37236     idToCssName : function(s)
37237     {
37238         return s.replace(/[^a-z0-9]+/ig, '-');
37239     },
37240
37241     getHeaderCell : function(index){
37242         return Roo.DomQuery.select(this.headerSelector)[index];
37243     },
37244
37245     getHeaderCellMeasure : function(index){
37246         return this.getHeaderCell(index).firstChild;
37247     },
37248
37249     getHeaderCellText : function(index){
37250         return this.getHeaderCell(index).firstChild.firstChild;
37251     },
37252
37253     getLockedTable : function(){
37254         return this.lockedBody.dom.firstChild;
37255     },
37256
37257     getBodyTable : function(){
37258         return this.mainBody.dom.firstChild;
37259     },
37260
37261     getLockedRow : function(index){
37262         return this.getLockedTable().rows[index];
37263     },
37264
37265     getRow : function(index){
37266         return this.getBodyTable().rows[index];
37267     },
37268
37269     getRowComposite : function(index){
37270         if(!this.rowEl){
37271             this.rowEl = new Roo.CompositeElementLite();
37272         }
37273         var els = [], lrow, mrow;
37274         if(lrow = this.getLockedRow(index)){
37275             els.push(lrow);
37276         }
37277         if(mrow = this.getRow(index)){
37278             els.push(mrow);
37279         }
37280         this.rowEl.elements = els;
37281         return this.rowEl;
37282     },
37283     /**
37284      * Gets the 'td' of the cell
37285      * 
37286      * @param {Integer} rowIndex row to select
37287      * @param {Integer} colIndex column to select
37288      * 
37289      * @return {Object} 
37290      */
37291     getCell : function(rowIndex, colIndex){
37292         var locked = this.cm.getLockedCount();
37293         var source;
37294         if(colIndex < locked){
37295             source = this.lockedBody.dom.firstChild;
37296         }else{
37297             source = this.mainBody.dom.firstChild;
37298             colIndex -= locked;
37299         }
37300         return source.rows[rowIndex].childNodes[colIndex];
37301     },
37302
37303     getCellText : function(rowIndex, colIndex){
37304         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37305     },
37306
37307     getCellBox : function(cell){
37308         var b = this.fly(cell).getBox();
37309         if(Roo.isOpera){ // opera fails to report the Y
37310             b.y = cell.offsetTop + this.mainBody.getY();
37311         }
37312         return b;
37313     },
37314
37315     getCellIndex : function(cell){
37316         var id = String(cell.className).match(this.cellRE);
37317         if(id){
37318             return parseInt(id[1], 10);
37319         }
37320         return 0;
37321     },
37322
37323     findHeaderIndex : function(n){
37324         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37325         return r ? this.getCellIndex(r) : false;
37326     },
37327
37328     findHeaderCell : function(n){
37329         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37330         return r ? r : false;
37331     },
37332
37333     findRowIndex : function(n){
37334         if(!n){
37335             return false;
37336         }
37337         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37338         return r ? r.rowIndex : false;
37339     },
37340
37341     findCellIndex : function(node){
37342         var stop = this.el.dom;
37343         while(node && node != stop){
37344             if(this.findRE.test(node.className)){
37345                 return this.getCellIndex(node);
37346             }
37347             node = node.parentNode;
37348         }
37349         return false;
37350     },
37351
37352     getColumnId : function(index){
37353         return this.cm.getColumnId(index);
37354     },
37355
37356     getSplitters : function()
37357     {
37358         if(this.splitterSelector){
37359            return Roo.DomQuery.select(this.splitterSelector);
37360         }else{
37361             return null;
37362       }
37363     },
37364
37365     getSplitter : function(index){
37366         return this.getSplitters()[index];
37367     },
37368
37369     onRowOver : function(e, t){
37370         var row;
37371         if((row = this.findRowIndex(t)) !== false){
37372             this.getRowComposite(row).addClass("x-grid-row-over");
37373         }
37374     },
37375
37376     onRowOut : function(e, t){
37377         var row;
37378         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37379             this.getRowComposite(row).removeClass("x-grid-row-over");
37380         }
37381     },
37382
37383     renderHeaders : function(){
37384         var cm = this.cm;
37385         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37386         var cb = [], lb = [], sb = [], lsb = [], p = {};
37387         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37388             p.cellId = "x-grid-hd-0-" + i;
37389             p.splitId = "x-grid-csplit-0-" + i;
37390             p.id = cm.getColumnId(i);
37391             p.title = cm.getColumnTooltip(i) || "";
37392             p.value = cm.getColumnHeader(i) || "";
37393             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37394             if(!cm.isLocked(i)){
37395                 cb[cb.length] = ct.apply(p);
37396                 sb[sb.length] = st.apply(p);
37397             }else{
37398                 lb[lb.length] = ct.apply(p);
37399                 lsb[lsb.length] = st.apply(p);
37400             }
37401         }
37402         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37403                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37404     },
37405
37406     updateHeaders : function(){
37407         var html = this.renderHeaders();
37408         this.lockedHd.update(html[0]);
37409         this.mainHd.update(html[1]);
37410     },
37411
37412     /**
37413      * Focuses the specified row.
37414      * @param {Number} row The row index
37415      */
37416     focusRow : function(row)
37417     {
37418         //Roo.log('GridView.focusRow');
37419         var x = this.scroller.dom.scrollLeft;
37420         this.focusCell(row, 0, false);
37421         this.scroller.dom.scrollLeft = x;
37422     },
37423
37424     /**
37425      * Focuses the specified cell.
37426      * @param {Number} row The row index
37427      * @param {Number} col The column index
37428      * @param {Boolean} hscroll false to disable horizontal scrolling
37429      */
37430     focusCell : function(row, col, hscroll)
37431     {
37432         //Roo.log('GridView.focusCell');
37433         var el = this.ensureVisible(row, col, hscroll);
37434         this.focusEl.alignTo(el, "tl-tl");
37435         if(Roo.isGecko){
37436             this.focusEl.focus();
37437         }else{
37438             this.focusEl.focus.defer(1, this.focusEl);
37439         }
37440     },
37441
37442     /**
37443      * Scrolls the specified cell into view
37444      * @param {Number} row The row index
37445      * @param {Number} col The column index
37446      * @param {Boolean} hscroll false to disable horizontal scrolling
37447      */
37448     ensureVisible : function(row, col, hscroll)
37449     {
37450         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37451         //return null; //disable for testing.
37452         if(typeof row != "number"){
37453             row = row.rowIndex;
37454         }
37455         if(row < 0 && row >= this.ds.getCount()){
37456             return  null;
37457         }
37458         col = (col !== undefined ? col : 0);
37459         var cm = this.grid.colModel;
37460         while(cm.isHidden(col)){
37461             col++;
37462         }
37463
37464         var el = this.getCell(row, col);
37465         if(!el){
37466             return null;
37467         }
37468         var c = this.scroller.dom;
37469
37470         var ctop = parseInt(el.offsetTop, 10);
37471         var cleft = parseInt(el.offsetLeft, 10);
37472         var cbot = ctop + el.offsetHeight;
37473         var cright = cleft + el.offsetWidth;
37474         
37475         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37476         var stop = parseInt(c.scrollTop, 10);
37477         var sleft = parseInt(c.scrollLeft, 10);
37478         var sbot = stop + ch;
37479         var sright = sleft + c.clientWidth;
37480         /*
37481         Roo.log('GridView.ensureVisible:' +
37482                 ' ctop:' + ctop +
37483                 ' c.clientHeight:' + c.clientHeight +
37484                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37485                 ' stop:' + stop +
37486                 ' cbot:' + cbot +
37487                 ' sbot:' + sbot +
37488                 ' ch:' + ch  
37489                 );
37490         */
37491         if(ctop < stop){
37492              c.scrollTop = ctop;
37493             //Roo.log("set scrolltop to ctop DISABLE?");
37494         }else if(cbot > sbot){
37495             //Roo.log("set scrolltop to cbot-ch");
37496             c.scrollTop = cbot-ch;
37497         }
37498         
37499         if(hscroll !== false){
37500             if(cleft < sleft){
37501                 c.scrollLeft = cleft;
37502             }else if(cright > sright){
37503                 c.scrollLeft = cright-c.clientWidth;
37504             }
37505         }
37506          
37507         return el;
37508     },
37509
37510     updateColumns : function(){
37511         this.grid.stopEditing();
37512         var cm = this.grid.colModel, colIds = this.getColumnIds();
37513         //var totalWidth = cm.getTotalWidth();
37514         var pos = 0;
37515         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37516             //if(cm.isHidden(i)) continue;
37517             var w = cm.getColumnWidth(i);
37518             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37519             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37520         }
37521         this.updateSplitters();
37522     },
37523
37524     generateRules : function(cm){
37525         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37526         Roo.util.CSS.removeStyleSheet(rulesId);
37527         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37528             var cid = cm.getColumnId(i);
37529             var align = '';
37530             if(cm.config[i].align){
37531                 align = 'text-align:'+cm.config[i].align+';';
37532             }
37533             var hidden = '';
37534             if(cm.isHidden(i)){
37535                 hidden = 'display:none;';
37536             }
37537             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37538             ruleBuf.push(
37539                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37540                     this.hdSelector, cid, " {\n", align, width, "}\n",
37541                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37542                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37543         }
37544         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37545     },
37546
37547     updateSplitters : function(){
37548         var cm = this.cm, s = this.getSplitters();
37549         if(s){ // splitters not created yet
37550             var pos = 0, locked = true;
37551             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37552                 if(cm.isHidden(i)) continue;
37553                 var w = cm.getColumnWidth(i); // make sure it's a number
37554                 if(!cm.isLocked(i) && locked){
37555                     pos = 0;
37556                     locked = false;
37557                 }
37558                 pos += w;
37559                 s[i].style.left = (pos-this.splitOffset) + "px";
37560             }
37561         }
37562     },
37563
37564     handleHiddenChange : function(colModel, colIndex, hidden){
37565         if(hidden){
37566             this.hideColumn(colIndex);
37567         }else{
37568             this.unhideColumn(colIndex);
37569         }
37570     },
37571
37572     hideColumn : function(colIndex){
37573         var cid = this.getColumnId(colIndex);
37574         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37575         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37576         if(Roo.isSafari){
37577             this.updateHeaders();
37578         }
37579         this.updateSplitters();
37580         this.layout();
37581     },
37582
37583     unhideColumn : function(colIndex){
37584         var cid = this.getColumnId(colIndex);
37585         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37586         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37587
37588         if(Roo.isSafari){
37589             this.updateHeaders();
37590         }
37591         this.updateSplitters();
37592         this.layout();
37593     },
37594
37595     insertRows : function(dm, firstRow, lastRow, isUpdate){
37596         if(firstRow == 0 && lastRow == dm.getCount()-1){
37597             this.refresh();
37598         }else{
37599             if(!isUpdate){
37600                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37601             }
37602             var s = this.getScrollState();
37603             var markup = this.renderRows(firstRow, lastRow);
37604             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37605             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37606             this.restoreScroll(s);
37607             if(!isUpdate){
37608                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37609                 this.syncRowHeights(firstRow, lastRow);
37610                 this.stripeRows(firstRow);
37611                 this.layout();
37612             }
37613         }
37614     },
37615
37616     bufferRows : function(markup, target, index){
37617         var before = null, trows = target.rows, tbody = target.tBodies[0];
37618         if(index < trows.length){
37619             before = trows[index];
37620         }
37621         var b = document.createElement("div");
37622         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37623         var rows = b.firstChild.rows;
37624         for(var i = 0, len = rows.length; i < len; i++){
37625             if(before){
37626                 tbody.insertBefore(rows[0], before);
37627             }else{
37628                 tbody.appendChild(rows[0]);
37629             }
37630         }
37631         b.innerHTML = "";
37632         b = null;
37633     },
37634
37635     deleteRows : function(dm, firstRow, lastRow){
37636         if(dm.getRowCount()<1){
37637             this.fireEvent("beforerefresh", this);
37638             this.mainBody.update("");
37639             this.lockedBody.update("");
37640             this.fireEvent("refresh", this);
37641         }else{
37642             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37643             var bt = this.getBodyTable();
37644             var tbody = bt.firstChild;
37645             var rows = bt.rows;
37646             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37647                 tbody.removeChild(rows[firstRow]);
37648             }
37649             this.stripeRows(firstRow);
37650             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37651         }
37652     },
37653
37654     updateRows : function(dataSource, firstRow, lastRow){
37655         var s = this.getScrollState();
37656         this.refresh();
37657         this.restoreScroll(s);
37658     },
37659
37660     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37661         if(!noRefresh){
37662            this.refresh();
37663         }
37664         this.updateHeaderSortState();
37665     },
37666
37667     getScrollState : function(){
37668         
37669         var sb = this.scroller.dom;
37670         return {left: sb.scrollLeft, top: sb.scrollTop};
37671     },
37672
37673     stripeRows : function(startRow){
37674         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37675             return;
37676         }
37677         startRow = startRow || 0;
37678         var rows = this.getBodyTable().rows;
37679         var lrows = this.getLockedTable().rows;
37680         var cls = ' x-grid-row-alt ';
37681         for(var i = startRow, len = rows.length; i < len; i++){
37682             var row = rows[i], lrow = lrows[i];
37683             var isAlt = ((i+1) % 2 == 0);
37684             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37685             if(isAlt == hasAlt){
37686                 continue;
37687             }
37688             if(isAlt){
37689                 row.className += " x-grid-row-alt";
37690             }else{
37691                 row.className = row.className.replace("x-grid-row-alt", "");
37692             }
37693             if(lrow){
37694                 lrow.className = row.className;
37695             }
37696         }
37697     },
37698
37699     restoreScroll : function(state){
37700         //Roo.log('GridView.restoreScroll');
37701         var sb = this.scroller.dom;
37702         sb.scrollLeft = state.left;
37703         sb.scrollTop = state.top;
37704         this.syncScroll();
37705     },
37706
37707     syncScroll : function(){
37708         //Roo.log('GridView.syncScroll');
37709         var sb = this.scroller.dom;
37710         var sh = this.mainHd.dom;
37711         var bs = this.mainBody.dom;
37712         var lv = this.lockedBody.dom;
37713         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37714         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37715     },
37716
37717     handleScroll : function(e){
37718         this.syncScroll();
37719         var sb = this.scroller.dom;
37720         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37721         e.stopEvent();
37722     },
37723
37724     handleWheel : function(e){
37725         var d = e.getWheelDelta();
37726         this.scroller.dom.scrollTop -= d*22;
37727         // set this here to prevent jumpy scrolling on large tables
37728         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37729         e.stopEvent();
37730     },
37731
37732     renderRows : function(startRow, endRow){
37733         // pull in all the crap needed to render rows
37734         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37735         var colCount = cm.getColumnCount();
37736
37737         if(ds.getCount() < 1){
37738             return ["", ""];
37739         }
37740
37741         // build a map for all the columns
37742         var cs = [];
37743         for(var i = 0; i < colCount; i++){
37744             var name = cm.getDataIndex(i);
37745             cs[i] = {
37746                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37747                 renderer : cm.getRenderer(i),
37748                 id : cm.getColumnId(i),
37749                 locked : cm.isLocked(i)
37750             };
37751         }
37752
37753         startRow = startRow || 0;
37754         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37755
37756         // records to render
37757         var rs = ds.getRange(startRow, endRow);
37758
37759         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37760     },
37761
37762     // As much as I hate to duplicate code, this was branched because FireFox really hates
37763     // [].join("") on strings. The performance difference was substantial enough to
37764     // branch this function
37765     doRender : Roo.isGecko ?
37766             function(cs, rs, ds, startRow, colCount, stripe){
37767                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37768                 // buffers
37769                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37770                 
37771                 var hasListener = this.grid.hasListener('rowclass');
37772                 var rowcfg = {};
37773                 for(var j = 0, len = rs.length; j < len; j++){
37774                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37775                     for(var i = 0; i < colCount; i++){
37776                         c = cs[i];
37777                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37778                         p.id = c.id;
37779                         p.css = p.attr = "";
37780                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37781                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37782                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37783                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37784                         }
37785                         var markup = ct.apply(p);
37786                         if(!c.locked){
37787                             cb+= markup;
37788                         }else{
37789                             lcb+= markup;
37790                         }
37791                     }
37792                     var alt = [];
37793                     if(stripe && ((rowIndex+1) % 2 == 0)){
37794                         alt.push("x-grid-row-alt")
37795                     }
37796                     if(r.dirty){
37797                         alt.push(  " x-grid-dirty-row");
37798                     }
37799                     rp.cells = lcb;
37800                     if(this.getRowClass){
37801                         alt.push(this.getRowClass(r, rowIndex));
37802                     }
37803                     if (hasListener) {
37804                         rowcfg = {
37805                              
37806                             record: r,
37807                             rowIndex : rowIndex,
37808                             rowClass : ''
37809                         }
37810                         this.grid.fireEvent('rowclass', this, rowcfg);
37811                         alt.push(rowcfg.rowClass);
37812                     }
37813                     rp.alt = alt.join(" ");
37814                     lbuf+= rt.apply(rp);
37815                     rp.cells = cb;
37816                     buf+=  rt.apply(rp);
37817                 }
37818                 return [lbuf, buf];
37819             } :
37820             function(cs, rs, ds, startRow, colCount, stripe){
37821                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37822                 // buffers
37823                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37824                 var hasListener = this.grid.hasListener('rowclass');
37825  
37826                 var rowcfg = {};
37827                 for(var j = 0, len = rs.length; j < len; j++){
37828                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37829                     for(var i = 0; i < colCount; i++){
37830                         c = cs[i];
37831                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37832                         p.id = c.id;
37833                         p.css = p.attr = "";
37834                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37835                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37836                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37837                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37838                         }
37839                         
37840                         var markup = ct.apply(p);
37841                         if(!c.locked){
37842                             cb[cb.length] = markup;
37843                         }else{
37844                             lcb[lcb.length] = markup;
37845                         }
37846                     }
37847                     var alt = [];
37848                     if(stripe && ((rowIndex+1) % 2 == 0)){
37849                         alt.push( "x-grid-row-alt");
37850                     }
37851                     if(r.dirty){
37852                         alt.push(" x-grid-dirty-row");
37853                     }
37854                     rp.cells = lcb;
37855                     if(this.getRowClass){
37856                         alt.push( this.getRowClass(r, rowIndex));
37857                     }
37858                     if (hasListener) {
37859                         rowcfg = {
37860                              
37861                             record: r,
37862                             rowIndex : rowIndex,
37863                             rowClass : ''
37864                         }
37865                         this.grid.fireEvent('rowclass', this, rowcfg);
37866                         alt.push(rowcfg.rowClass);
37867                     }
37868                     rp.alt = alt.join(" ");
37869                     rp.cells = lcb.join("");
37870                     lbuf[lbuf.length] = rt.apply(rp);
37871                     rp.cells = cb.join("");
37872                     buf[buf.length] =  rt.apply(rp);
37873                 }
37874                 return [lbuf.join(""), buf.join("")];
37875             },
37876
37877     renderBody : function(){
37878         var markup = this.renderRows();
37879         var bt = this.templates.body;
37880         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37881     },
37882
37883     /**
37884      * Refreshes the grid
37885      * @param {Boolean} headersToo
37886      */
37887     refresh : function(headersToo){
37888         this.fireEvent("beforerefresh", this);
37889         this.grid.stopEditing();
37890         var result = this.renderBody();
37891         this.lockedBody.update(result[0]);
37892         this.mainBody.update(result[1]);
37893         if(headersToo === true){
37894             this.updateHeaders();
37895             this.updateColumns();
37896             this.updateSplitters();
37897             this.updateHeaderSortState();
37898         }
37899         this.syncRowHeights();
37900         this.layout();
37901         this.fireEvent("refresh", this);
37902     },
37903
37904     handleColumnMove : function(cm, oldIndex, newIndex){
37905         this.indexMap = null;
37906         var s = this.getScrollState();
37907         this.refresh(true);
37908         this.restoreScroll(s);
37909         this.afterMove(newIndex);
37910     },
37911
37912     afterMove : function(colIndex){
37913         if(this.enableMoveAnim && Roo.enableFx){
37914             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37915         }
37916         // if multisort - fix sortOrder, and reload..
37917         if (this.grid.dataSource.multiSort) {
37918             // the we can call sort again..
37919             var dm = this.grid.dataSource;
37920             var cm = this.grid.colModel;
37921             var so = [];
37922             for(var i = 0; i < cm.config.length; i++ ) {
37923                 
37924                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37925                     continue; // dont' bother, it's not in sort list or being set.
37926                 }
37927                 
37928                 so.push(cm.config[i].dataIndex);
37929             };
37930             dm.sortOrder = so;
37931             dm.load(dm.lastOptions);
37932             
37933             
37934         }
37935         
37936     },
37937
37938     updateCell : function(dm, rowIndex, dataIndex){
37939         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37940         if(typeof colIndex == "undefined"){ // not present in grid
37941             return;
37942         }
37943         var cm = this.grid.colModel;
37944         var cell = this.getCell(rowIndex, colIndex);
37945         var cellText = this.getCellText(rowIndex, colIndex);
37946
37947         var p = {
37948             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37949             id : cm.getColumnId(colIndex),
37950             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37951         };
37952         var renderer = cm.getRenderer(colIndex);
37953         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37954         if(typeof val == "undefined" || val === "") val = "&#160;";
37955         cellText.innerHTML = val;
37956         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37957         this.syncRowHeights(rowIndex, rowIndex);
37958     },
37959
37960     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37961         var maxWidth = 0;
37962         if(this.grid.autoSizeHeaders){
37963             var h = this.getHeaderCellMeasure(colIndex);
37964             maxWidth = Math.max(maxWidth, h.scrollWidth);
37965         }
37966         var tb, index;
37967         if(this.cm.isLocked(colIndex)){
37968             tb = this.getLockedTable();
37969             index = colIndex;
37970         }else{
37971             tb = this.getBodyTable();
37972             index = colIndex - this.cm.getLockedCount();
37973         }
37974         if(tb && tb.rows){
37975             var rows = tb.rows;
37976             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37977             for(var i = 0; i < stopIndex; i++){
37978                 var cell = rows[i].childNodes[index].firstChild;
37979                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37980             }
37981         }
37982         return maxWidth + /*margin for error in IE*/ 5;
37983     },
37984     /**
37985      * Autofit a column to its content.
37986      * @param {Number} colIndex
37987      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37988      */
37989      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37990          if(this.cm.isHidden(colIndex)){
37991              return; // can't calc a hidden column
37992          }
37993         if(forceMinSize){
37994             var cid = this.cm.getColumnId(colIndex);
37995             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37996            if(this.grid.autoSizeHeaders){
37997                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37998            }
37999         }
38000         var newWidth = this.calcColumnWidth(colIndex);
38001         this.cm.setColumnWidth(colIndex,
38002             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38003         if(!suppressEvent){
38004             this.grid.fireEvent("columnresize", colIndex, newWidth);
38005         }
38006     },
38007
38008     /**
38009      * Autofits all columns to their content and then expands to fit any extra space in the grid
38010      */
38011      autoSizeColumns : function(){
38012         var cm = this.grid.colModel;
38013         var colCount = cm.getColumnCount();
38014         for(var i = 0; i < colCount; i++){
38015             this.autoSizeColumn(i, true, true);
38016         }
38017         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38018             this.fitColumns();
38019         }else{
38020             this.updateColumns();
38021             this.layout();
38022         }
38023     },
38024
38025     /**
38026      * Autofits all columns to the grid's width proportionate with their current size
38027      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38028      */
38029     fitColumns : function(reserveScrollSpace){
38030         var cm = this.grid.colModel;
38031         var colCount = cm.getColumnCount();
38032         var cols = [];
38033         var width = 0;
38034         var i, w;
38035         for (i = 0; i < colCount; i++){
38036             if(!cm.isHidden(i) && !cm.isFixed(i)){
38037                 w = cm.getColumnWidth(i);
38038                 cols.push(i);
38039                 cols.push(w);
38040                 width += w;
38041             }
38042         }
38043         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38044         if(reserveScrollSpace){
38045             avail -= 17;
38046         }
38047         var frac = (avail - cm.getTotalWidth())/width;
38048         while (cols.length){
38049             w = cols.pop();
38050             i = cols.pop();
38051             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38052         }
38053         this.updateColumns();
38054         this.layout();
38055     },
38056
38057     onRowSelect : function(rowIndex){
38058         var row = this.getRowComposite(rowIndex);
38059         row.addClass("x-grid-row-selected");
38060     },
38061
38062     onRowDeselect : function(rowIndex){
38063         var row = this.getRowComposite(rowIndex);
38064         row.removeClass("x-grid-row-selected");
38065     },
38066
38067     onCellSelect : function(row, col){
38068         var cell = this.getCell(row, col);
38069         if(cell){
38070             Roo.fly(cell).addClass("x-grid-cell-selected");
38071         }
38072     },
38073
38074     onCellDeselect : function(row, col){
38075         var cell = this.getCell(row, col);
38076         if(cell){
38077             Roo.fly(cell).removeClass("x-grid-cell-selected");
38078         }
38079     },
38080
38081     updateHeaderSortState : function(){
38082         
38083         // sort state can be single { field: xxx, direction : yyy}
38084         // or   { xxx=>ASC , yyy : DESC ..... }
38085         
38086         var mstate = {};
38087         if (!this.ds.multiSort) { 
38088             var state = this.ds.getSortState();
38089             if(!state){
38090                 return;
38091             }
38092             mstate[state.field] = state.direction;
38093             // FIXME... - this is not used here.. but might be elsewhere..
38094             this.sortState = state;
38095             
38096         } else {
38097             mstate = this.ds.sortToggle;
38098         }
38099         //remove existing sort classes..
38100         
38101         var sc = this.sortClasses;
38102         var hds = this.el.select(this.headerSelector).removeClass(sc);
38103         
38104         for(var f in mstate) {
38105         
38106             var sortColumn = this.cm.findColumnIndex(f);
38107             
38108             if(sortColumn != -1){
38109                 var sortDir = mstate[f];        
38110                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38111             }
38112         }
38113         
38114          
38115         
38116     },
38117
38118
38119     handleHeaderClick : function(g, index,e){
38120         
38121         Roo.log("header click");
38122         
38123         if (Roo.isTouch) {
38124             // touch events on header are handled by context
38125             this.handleHdCtx(g,index,e);
38126             return;
38127         }
38128         
38129         
38130         if(this.headersDisabled){
38131             return;
38132         }
38133         var dm = g.dataSource, cm = g.colModel;
38134         if(!cm.isSortable(index)){
38135             return;
38136         }
38137         g.stopEditing();
38138         
38139         if (dm.multiSort) {
38140             // update the sortOrder
38141             var so = [];
38142             for(var i = 0; i < cm.config.length; i++ ) {
38143                 
38144                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38145                     continue; // dont' bother, it's not in sort list or being set.
38146                 }
38147                 
38148                 so.push(cm.config[i].dataIndex);
38149             };
38150             dm.sortOrder = so;
38151         }
38152         
38153         
38154         dm.sort(cm.getDataIndex(index));
38155     },
38156
38157
38158     destroy : function(){
38159         if(this.colMenu){
38160             this.colMenu.removeAll();
38161             Roo.menu.MenuMgr.unregister(this.colMenu);
38162             this.colMenu.getEl().remove();
38163             delete this.colMenu;
38164         }
38165         if(this.hmenu){
38166             this.hmenu.removeAll();
38167             Roo.menu.MenuMgr.unregister(this.hmenu);
38168             this.hmenu.getEl().remove();
38169             delete this.hmenu;
38170         }
38171         if(this.grid.enableColumnMove){
38172             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38173             if(dds){
38174                 for(var dd in dds){
38175                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38176                         var elid = dds[dd].dragElId;
38177                         dds[dd].unreg();
38178                         Roo.get(elid).remove();
38179                     } else if(dds[dd].config.isTarget){
38180                         dds[dd].proxyTop.remove();
38181                         dds[dd].proxyBottom.remove();
38182                         dds[dd].unreg();
38183                     }
38184                     if(Roo.dd.DDM.locationCache[dd]){
38185                         delete Roo.dd.DDM.locationCache[dd];
38186                     }
38187                 }
38188                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38189             }
38190         }
38191         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38192         this.bind(null, null);
38193         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38194     },
38195
38196     handleLockChange : function(){
38197         this.refresh(true);
38198     },
38199
38200     onDenyColumnLock : function(){
38201
38202     },
38203
38204     onDenyColumnHide : function(){
38205
38206     },
38207
38208     handleHdMenuClick : function(item){
38209         var index = this.hdCtxIndex;
38210         var cm = this.cm, ds = this.ds;
38211         switch(item.id){
38212             case "asc":
38213                 ds.sort(cm.getDataIndex(index), "ASC");
38214                 break;
38215             case "desc":
38216                 ds.sort(cm.getDataIndex(index), "DESC");
38217                 break;
38218             case "lock":
38219                 var lc = cm.getLockedCount();
38220                 if(cm.getColumnCount(true) <= lc+1){
38221                     this.onDenyColumnLock();
38222                     return;
38223                 }
38224                 if(lc != index){
38225                     cm.setLocked(index, true, true);
38226                     cm.moveColumn(index, lc);
38227                     this.grid.fireEvent("columnmove", index, lc);
38228                 }else{
38229                     cm.setLocked(index, true);
38230                 }
38231             break;
38232             case "unlock":
38233                 var lc = cm.getLockedCount();
38234                 if((lc-1) != index){
38235                     cm.setLocked(index, false, true);
38236                     cm.moveColumn(index, lc-1);
38237                     this.grid.fireEvent("columnmove", index, lc-1);
38238                 }else{
38239                     cm.setLocked(index, false);
38240                 }
38241             break;
38242             case 'wider': // used to expand cols on touch..
38243             case 'narrow':
38244                 var cw = cm.getColumnWidth(index);
38245                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38246                 cw = Math.max(0, cw);
38247                 cw = Math.min(cw,4000);
38248                 cm.setColumnWidth(index, cw);
38249                 break;
38250                 
38251             default:
38252                 index = cm.getIndexById(item.id.substr(4));
38253                 if(index != -1){
38254                     if(item.checked && cm.getColumnCount(true) <= 1){
38255                         this.onDenyColumnHide();
38256                         return false;
38257                     }
38258                     cm.setHidden(index, item.checked);
38259                 }
38260         }
38261         return true;
38262     },
38263
38264     beforeColMenuShow : function(){
38265         var cm = this.cm,  colCount = cm.getColumnCount();
38266         this.colMenu.removeAll();
38267         for(var i = 0; i < colCount; i++){
38268             this.colMenu.add(new Roo.menu.CheckItem({
38269                 id: "col-"+cm.getColumnId(i),
38270                 text: cm.getColumnHeader(i),
38271                 checked: !cm.isHidden(i),
38272                 hideOnClick:false
38273             }));
38274         }
38275     },
38276
38277     handleHdCtx : function(g, index, e){
38278         e.stopEvent();
38279         var hd = this.getHeaderCell(index);
38280         this.hdCtxIndex = index;
38281         var ms = this.hmenu.items, cm = this.cm;
38282         ms.get("asc").setDisabled(!cm.isSortable(index));
38283         ms.get("desc").setDisabled(!cm.isSortable(index));
38284         if(this.grid.enableColLock !== false){
38285             ms.get("lock").setDisabled(cm.isLocked(index));
38286             ms.get("unlock").setDisabled(!cm.isLocked(index));
38287         }
38288         this.hmenu.show(hd, "tl-bl");
38289     },
38290
38291     handleHdOver : function(e){
38292         var hd = this.findHeaderCell(e.getTarget());
38293         if(hd && !this.headersDisabled){
38294             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38295                this.fly(hd).addClass("x-grid-hd-over");
38296             }
38297         }
38298     },
38299
38300     handleHdOut : function(e){
38301         var hd = this.findHeaderCell(e.getTarget());
38302         if(hd){
38303             this.fly(hd).removeClass("x-grid-hd-over");
38304         }
38305     },
38306
38307     handleSplitDblClick : function(e, t){
38308         var i = this.getCellIndex(t);
38309         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38310             this.autoSizeColumn(i, true);
38311             this.layout();
38312         }
38313     },
38314
38315     render : function(){
38316
38317         var cm = this.cm;
38318         var colCount = cm.getColumnCount();
38319
38320         if(this.grid.monitorWindowResize === true){
38321             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38322         }
38323         var header = this.renderHeaders();
38324         var body = this.templates.body.apply({rows:""});
38325         var html = this.templates.master.apply({
38326             lockedBody: body,
38327             body: body,
38328             lockedHeader: header[0],
38329             header: header[1]
38330         });
38331
38332         //this.updateColumns();
38333
38334         this.grid.getGridEl().dom.innerHTML = html;
38335
38336         this.initElements();
38337         
38338         // a kludge to fix the random scolling effect in webkit
38339         this.el.on("scroll", function() {
38340             this.el.dom.scrollTop=0; // hopefully not recursive..
38341         },this);
38342
38343         this.scroller.on("scroll", this.handleScroll, this);
38344         this.lockedBody.on("mousewheel", this.handleWheel, this);
38345         this.mainBody.on("mousewheel", this.handleWheel, this);
38346
38347         this.mainHd.on("mouseover", this.handleHdOver, this);
38348         this.mainHd.on("mouseout", this.handleHdOut, this);
38349         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38350                 {delegate: "."+this.splitClass});
38351
38352         this.lockedHd.on("mouseover", this.handleHdOver, this);
38353         this.lockedHd.on("mouseout", this.handleHdOut, this);
38354         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38355                 {delegate: "."+this.splitClass});
38356
38357         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38358             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38359         }
38360
38361         this.updateSplitters();
38362
38363         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38364             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38365             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38366         }
38367
38368         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38369             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38370             this.hmenu.add(
38371                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38372                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38373             );
38374             if(this.grid.enableColLock !== false){
38375                 this.hmenu.add('-',
38376                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38377                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38378                 );
38379             }
38380             if (Roo.isTouch) {
38381                  this.hmenu.add('-',
38382                     {id:"wider", text: this.columnsWiderText},
38383                     {id:"narrow", text: this.columnsNarrowText }
38384                 );
38385                 
38386                  
38387             }
38388             
38389             if(this.grid.enableColumnHide !== false){
38390
38391                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38392                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38393                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38394
38395                 this.hmenu.add('-',
38396                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38397                 );
38398             }
38399             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38400
38401             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38402         }
38403
38404         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38405             this.dd = new Roo.grid.GridDragZone(this.grid, {
38406                 ddGroup : this.grid.ddGroup || 'GridDD'
38407             });
38408             
38409         }
38410
38411         /*
38412         for(var i = 0; i < colCount; i++){
38413             if(cm.isHidden(i)){
38414                 this.hideColumn(i);
38415             }
38416             if(cm.config[i].align){
38417                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38418                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38419             }
38420         }*/
38421         
38422         this.updateHeaderSortState();
38423
38424         this.beforeInitialResize();
38425         this.layout(true);
38426
38427         // two part rendering gives faster view to the user
38428         this.renderPhase2.defer(1, this);
38429     },
38430
38431     renderPhase2 : function(){
38432         // render the rows now
38433         this.refresh();
38434         if(this.grid.autoSizeColumns){
38435             this.autoSizeColumns();
38436         }
38437     },
38438
38439     beforeInitialResize : function(){
38440
38441     },
38442
38443     onColumnSplitterMoved : function(i, w){
38444         this.userResized = true;
38445         var cm = this.grid.colModel;
38446         cm.setColumnWidth(i, w, true);
38447         var cid = cm.getColumnId(i);
38448         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38449         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38450         this.updateSplitters();
38451         this.layout();
38452         this.grid.fireEvent("columnresize", i, w);
38453     },
38454
38455     syncRowHeights : function(startIndex, endIndex){
38456         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38457             startIndex = startIndex || 0;
38458             var mrows = this.getBodyTable().rows;
38459             var lrows = this.getLockedTable().rows;
38460             var len = mrows.length-1;
38461             endIndex = Math.min(endIndex || len, len);
38462             for(var i = startIndex; i <= endIndex; i++){
38463                 var m = mrows[i], l = lrows[i];
38464                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38465                 m.style.height = l.style.height = h + "px";
38466             }
38467         }
38468     },
38469
38470     layout : function(initialRender, is2ndPass){
38471         var g = this.grid;
38472         var auto = g.autoHeight;
38473         var scrollOffset = 16;
38474         var c = g.getGridEl(), cm = this.cm,
38475                 expandCol = g.autoExpandColumn,
38476                 gv = this;
38477         //c.beginMeasure();
38478
38479         if(!c.dom.offsetWidth){ // display:none?
38480             if(initialRender){
38481                 this.lockedWrap.show();
38482                 this.mainWrap.show();
38483             }
38484             return;
38485         }
38486
38487         var hasLock = this.cm.isLocked(0);
38488
38489         var tbh = this.headerPanel.getHeight();
38490         var bbh = this.footerPanel.getHeight();
38491
38492         if(auto){
38493             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38494             var newHeight = ch + c.getBorderWidth("tb");
38495             if(g.maxHeight){
38496                 newHeight = Math.min(g.maxHeight, newHeight);
38497             }
38498             c.setHeight(newHeight);
38499         }
38500
38501         if(g.autoWidth){
38502             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38503         }
38504
38505         var s = this.scroller;
38506
38507         var csize = c.getSize(true);
38508
38509         this.el.setSize(csize.width, csize.height);
38510
38511         this.headerPanel.setWidth(csize.width);
38512         this.footerPanel.setWidth(csize.width);
38513
38514         var hdHeight = this.mainHd.getHeight();
38515         var vw = csize.width;
38516         var vh = csize.height - (tbh + bbh);
38517
38518         s.setSize(vw, vh);
38519
38520         var bt = this.getBodyTable();
38521         var ltWidth = hasLock ?
38522                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38523
38524         var scrollHeight = bt.offsetHeight;
38525         var scrollWidth = ltWidth + bt.offsetWidth;
38526         var vscroll = false, hscroll = false;
38527
38528         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38529
38530         var lw = this.lockedWrap, mw = this.mainWrap;
38531         var lb = this.lockedBody, mb = this.mainBody;
38532
38533         setTimeout(function(){
38534             var t = s.dom.offsetTop;
38535             var w = s.dom.clientWidth,
38536                 h = s.dom.clientHeight;
38537
38538             lw.setTop(t);
38539             lw.setSize(ltWidth, h);
38540
38541             mw.setLeftTop(ltWidth, t);
38542             mw.setSize(w-ltWidth, h);
38543
38544             lb.setHeight(h-hdHeight);
38545             mb.setHeight(h-hdHeight);
38546
38547             if(is2ndPass !== true && !gv.userResized && expandCol){
38548                 // high speed resize without full column calculation
38549                 
38550                 var ci = cm.getIndexById(expandCol);
38551                 if (ci < 0) {
38552                     ci = cm.findColumnIndex(expandCol);
38553                 }
38554                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38555                 var expandId = cm.getColumnId(ci);
38556                 var  tw = cm.getTotalWidth(false);
38557                 var currentWidth = cm.getColumnWidth(ci);
38558                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38559                 if(currentWidth != cw){
38560                     cm.setColumnWidth(ci, cw, true);
38561                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38562                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38563                     gv.updateSplitters();
38564                     gv.layout(false, true);
38565                 }
38566             }
38567
38568             if(initialRender){
38569                 lw.show();
38570                 mw.show();
38571             }
38572             //c.endMeasure();
38573         }, 10);
38574     },
38575
38576     onWindowResize : function(){
38577         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38578             return;
38579         }
38580         this.layout();
38581     },
38582
38583     appendFooter : function(parentEl){
38584         return null;
38585     },
38586
38587     sortAscText : "Sort Ascending",
38588     sortDescText : "Sort Descending",
38589     lockText : "Lock Column",
38590     unlockText : "Unlock Column",
38591     columnsText : "Columns",
38592  
38593     columnsWiderText : "Wider",
38594     columnsNarrowText : "Thinner"
38595 });
38596
38597
38598 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38599     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38600     this.proxy.el.addClass('x-grid3-col-dd');
38601 };
38602
38603 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38604     handleMouseDown : function(e){
38605
38606     },
38607
38608     callHandleMouseDown : function(e){
38609         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38610     }
38611 });
38612 /*
38613  * Based on:
38614  * Ext JS Library 1.1.1
38615  * Copyright(c) 2006-2007, Ext JS, LLC.
38616  *
38617  * Originally Released Under LGPL - original licence link has changed is not relivant.
38618  *
38619  * Fork - LGPL
38620  * <script type="text/javascript">
38621  */
38622  
38623 // private
38624 // This is a support class used internally by the Grid components
38625 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38626     this.grid = grid;
38627     this.view = grid.getView();
38628     this.proxy = this.view.resizeProxy;
38629     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38630         "gridSplitters" + this.grid.getGridEl().id, {
38631         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38632     });
38633     this.setHandleElId(Roo.id(hd));
38634     this.setOuterHandleElId(Roo.id(hd2));
38635     this.scroll = false;
38636 };
38637 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38638     fly: Roo.Element.fly,
38639
38640     b4StartDrag : function(x, y){
38641         this.view.headersDisabled = true;
38642         this.proxy.setHeight(this.view.mainWrap.getHeight());
38643         var w = this.cm.getColumnWidth(this.cellIndex);
38644         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38645         this.resetConstraints();
38646         this.setXConstraint(minw, 1000);
38647         this.setYConstraint(0, 0);
38648         this.minX = x - minw;
38649         this.maxX = x + 1000;
38650         this.startPos = x;
38651         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38652     },
38653
38654
38655     handleMouseDown : function(e){
38656         ev = Roo.EventObject.setEvent(e);
38657         var t = this.fly(ev.getTarget());
38658         if(t.hasClass("x-grid-split")){
38659             this.cellIndex = this.view.getCellIndex(t.dom);
38660             this.split = t.dom;
38661             this.cm = this.grid.colModel;
38662             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38663                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38664             }
38665         }
38666     },
38667
38668     endDrag : function(e){
38669         this.view.headersDisabled = false;
38670         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38671         var diff = endX - this.startPos;
38672         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38673     },
38674
38675     autoOffset : function(){
38676         this.setDelta(0,0);
38677     }
38678 });/*
38679  * Based on:
38680  * Ext JS Library 1.1.1
38681  * Copyright(c) 2006-2007, Ext JS, LLC.
38682  *
38683  * Originally Released Under LGPL - original licence link has changed is not relivant.
38684  *
38685  * Fork - LGPL
38686  * <script type="text/javascript">
38687  */
38688  
38689 // private
38690 // This is a support class used internally by the Grid components
38691 Roo.grid.GridDragZone = function(grid, config){
38692     this.view = grid.getView();
38693     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38694     if(this.view.lockedBody){
38695         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38696         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38697     }
38698     this.scroll = false;
38699     this.grid = grid;
38700     this.ddel = document.createElement('div');
38701     this.ddel.className = 'x-grid-dd-wrap';
38702 };
38703
38704 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38705     ddGroup : "GridDD",
38706
38707     getDragData : function(e){
38708         var t = Roo.lib.Event.getTarget(e);
38709         var rowIndex = this.view.findRowIndex(t);
38710         var sm = this.grid.selModel;
38711             
38712         //Roo.log(rowIndex);
38713         
38714         if (sm.getSelectedCell) {
38715             // cell selection..
38716             if (!sm.getSelectedCell()) {
38717                 return false;
38718             }
38719             if (rowIndex != sm.getSelectedCell()[0]) {
38720                 return false;
38721             }
38722         
38723         }
38724         
38725         if(rowIndex !== false){
38726             
38727             // if editorgrid.. 
38728             
38729             
38730             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38731                
38732             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38733               //  
38734             //}
38735             if (e.hasModifier()){
38736                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38737             }
38738             
38739             Roo.log("getDragData");
38740             
38741             return {
38742                 grid: this.grid,
38743                 ddel: this.ddel,
38744                 rowIndex: rowIndex,
38745                 selections:sm.getSelections ? sm.getSelections() : (
38746                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38747                 )
38748             };
38749         }
38750         return false;
38751     },
38752
38753     onInitDrag : function(e){
38754         var data = this.dragData;
38755         this.ddel.innerHTML = this.grid.getDragDropText();
38756         this.proxy.update(this.ddel);
38757         // fire start drag?
38758     },
38759
38760     afterRepair : function(){
38761         this.dragging = false;
38762     },
38763
38764     getRepairXY : function(e, data){
38765         return false;
38766     },
38767
38768     onEndDrag : function(data, e){
38769         // fire end drag?
38770     },
38771
38772     onValidDrop : function(dd, e, id){
38773         // fire drag drop?
38774         this.hideProxy();
38775     },
38776
38777     beforeInvalidDrop : function(e, id){
38778
38779     }
38780 });/*
38781  * Based on:
38782  * Ext JS Library 1.1.1
38783  * Copyright(c) 2006-2007, Ext JS, LLC.
38784  *
38785  * Originally Released Under LGPL - original licence link has changed is not relivant.
38786  *
38787  * Fork - LGPL
38788  * <script type="text/javascript">
38789  */
38790  
38791
38792 /**
38793  * @class Roo.grid.ColumnModel
38794  * @extends Roo.util.Observable
38795  * This is the default implementation of a ColumnModel used by the Grid. It defines
38796  * the columns in the grid.
38797  * <br>Usage:<br>
38798  <pre><code>
38799  var colModel = new Roo.grid.ColumnModel([
38800         {header: "Ticker", width: 60, sortable: true, locked: true},
38801         {header: "Company Name", width: 150, sortable: true},
38802         {header: "Market Cap.", width: 100, sortable: true},
38803         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38804         {header: "Employees", width: 100, sortable: true, resizable: false}
38805  ]);
38806  </code></pre>
38807  * <p>
38808  
38809  * The config options listed for this class are options which may appear in each
38810  * individual column definition.
38811  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38812  * @constructor
38813  * @param {Object} config An Array of column config objects. See this class's
38814  * config objects for details.
38815 */
38816 Roo.grid.ColumnModel = function(config){
38817         /**
38818      * The config passed into the constructor
38819      */
38820     this.config = config;
38821     this.lookup = {};
38822
38823     // if no id, create one
38824     // if the column does not have a dataIndex mapping,
38825     // map it to the order it is in the config
38826     for(var i = 0, len = config.length; i < len; i++){
38827         var c = config[i];
38828         if(typeof c.dataIndex == "undefined"){
38829             c.dataIndex = i;
38830         }
38831         if(typeof c.renderer == "string"){
38832             c.renderer = Roo.util.Format[c.renderer];
38833         }
38834         if(typeof c.id == "undefined"){
38835             c.id = Roo.id();
38836         }
38837         if(c.editor && c.editor.xtype){
38838             c.editor  = Roo.factory(c.editor, Roo.grid);
38839         }
38840         if(c.editor && c.editor.isFormField){
38841             c.editor = new Roo.grid.GridEditor(c.editor);
38842         }
38843         this.lookup[c.id] = c;
38844     }
38845
38846     /**
38847      * The width of columns which have no width specified (defaults to 100)
38848      * @type Number
38849      */
38850     this.defaultWidth = 100;
38851
38852     /**
38853      * Default sortable of columns which have no sortable specified (defaults to false)
38854      * @type Boolean
38855      */
38856     this.defaultSortable = false;
38857
38858     this.addEvents({
38859         /**
38860              * @event widthchange
38861              * Fires when the width of a column changes.
38862              * @param {ColumnModel} this
38863              * @param {Number} columnIndex The column index
38864              * @param {Number} newWidth The new width
38865              */
38866             "widthchange": true,
38867         /**
38868              * @event headerchange
38869              * Fires when the text of a header changes.
38870              * @param {ColumnModel} this
38871              * @param {Number} columnIndex The column index
38872              * @param {Number} newText The new header text
38873              */
38874             "headerchange": true,
38875         /**
38876              * @event hiddenchange
38877              * Fires when a column is hidden or "unhidden".
38878              * @param {ColumnModel} this
38879              * @param {Number} columnIndex The column index
38880              * @param {Boolean} hidden true if hidden, false otherwise
38881              */
38882             "hiddenchange": true,
38883             /**
38884          * @event columnmoved
38885          * Fires when a column is moved.
38886          * @param {ColumnModel} this
38887          * @param {Number} oldIndex
38888          * @param {Number} newIndex
38889          */
38890         "columnmoved" : true,
38891         /**
38892          * @event columlockchange
38893          * Fires when a column's locked state is changed
38894          * @param {ColumnModel} this
38895          * @param {Number} colIndex
38896          * @param {Boolean} locked true if locked
38897          */
38898         "columnlockchange" : true
38899     });
38900     Roo.grid.ColumnModel.superclass.constructor.call(this);
38901 };
38902 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38903     /**
38904      * @cfg {String} header The header text to display in the Grid view.
38905      */
38906     /**
38907      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38908      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38909      * specified, the column's index is used as an index into the Record's data Array.
38910      */
38911     /**
38912      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38913      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38914      */
38915     /**
38916      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38917      * Defaults to the value of the {@link #defaultSortable} property.
38918      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38919      */
38920     /**
38921      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38922      */
38923     /**
38924      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38925      */
38926     /**
38927      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38928      */
38929     /**
38930      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38931      */
38932     /**
38933      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38934      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38935      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38936      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38937      */
38938        /**
38939      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38940      */
38941     /**
38942      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38943      */
38944     /**
38945      * @cfg {String} cursor (Optional)
38946      */
38947     /**
38948      * @cfg {String} tooltip (Optional)
38949      */
38950     /**
38951      * Returns the id of the column at the specified index.
38952      * @param {Number} index The column index
38953      * @return {String} the id
38954      */
38955     getColumnId : function(index){
38956         return this.config[index].id;
38957     },
38958
38959     /**
38960      * Returns the column for a specified id.
38961      * @param {String} id The column id
38962      * @return {Object} the column
38963      */
38964     getColumnById : function(id){
38965         return this.lookup[id];
38966     },
38967
38968     
38969     /**
38970      * Returns the column for a specified dataIndex.
38971      * @param {String} dataIndex The column dataIndex
38972      * @return {Object|Boolean} the column or false if not found
38973      */
38974     getColumnByDataIndex: function(dataIndex){
38975         var index = this.findColumnIndex(dataIndex);
38976         return index > -1 ? this.config[index] : false;
38977     },
38978     
38979     /**
38980      * Returns the index for a specified column id.
38981      * @param {String} id The column id
38982      * @return {Number} the index, or -1 if not found
38983      */
38984     getIndexById : function(id){
38985         for(var i = 0, len = this.config.length; i < len; i++){
38986             if(this.config[i].id == id){
38987                 return i;
38988             }
38989         }
38990         return -1;
38991     },
38992     
38993     /**
38994      * Returns the index for a specified column dataIndex.
38995      * @param {String} dataIndex The column dataIndex
38996      * @return {Number} the index, or -1 if not found
38997      */
38998     
38999     findColumnIndex : function(dataIndex){
39000         for(var i = 0, len = this.config.length; i < len; i++){
39001             if(this.config[i].dataIndex == dataIndex){
39002                 return i;
39003             }
39004         }
39005         return -1;
39006     },
39007     
39008     
39009     moveColumn : function(oldIndex, newIndex){
39010         var c = this.config[oldIndex];
39011         this.config.splice(oldIndex, 1);
39012         this.config.splice(newIndex, 0, c);
39013         this.dataMap = null;
39014         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39015     },
39016
39017     isLocked : function(colIndex){
39018         return this.config[colIndex].locked === true;
39019     },
39020
39021     setLocked : function(colIndex, value, suppressEvent){
39022         if(this.isLocked(colIndex) == value){
39023             return;
39024         }
39025         this.config[colIndex].locked = value;
39026         if(!suppressEvent){
39027             this.fireEvent("columnlockchange", this, colIndex, value);
39028         }
39029     },
39030
39031     getTotalLockedWidth : function(){
39032         var totalWidth = 0;
39033         for(var i = 0; i < this.config.length; i++){
39034             if(this.isLocked(i) && !this.isHidden(i)){
39035                 this.totalWidth += this.getColumnWidth(i);
39036             }
39037         }
39038         return totalWidth;
39039     },
39040
39041     getLockedCount : function(){
39042         for(var i = 0, len = this.config.length; i < len; i++){
39043             if(!this.isLocked(i)){
39044                 return i;
39045             }
39046         }
39047     },
39048
39049     /**
39050      * Returns the number of columns.
39051      * @return {Number}
39052      */
39053     getColumnCount : function(visibleOnly){
39054         if(visibleOnly === true){
39055             var c = 0;
39056             for(var i = 0, len = this.config.length; i < len; i++){
39057                 if(!this.isHidden(i)){
39058                     c++;
39059                 }
39060             }
39061             return c;
39062         }
39063         return this.config.length;
39064     },
39065
39066     /**
39067      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39068      * @param {Function} fn
39069      * @param {Object} scope (optional)
39070      * @return {Array} result
39071      */
39072     getColumnsBy : function(fn, scope){
39073         var r = [];
39074         for(var i = 0, len = this.config.length; i < len; i++){
39075             var c = this.config[i];
39076             if(fn.call(scope||this, c, i) === true){
39077                 r[r.length] = c;
39078             }
39079         }
39080         return r;
39081     },
39082
39083     /**
39084      * Returns true if the specified column is sortable.
39085      * @param {Number} col The column index
39086      * @return {Boolean}
39087      */
39088     isSortable : function(col){
39089         if(typeof this.config[col].sortable == "undefined"){
39090             return this.defaultSortable;
39091         }
39092         return this.config[col].sortable;
39093     },
39094
39095     /**
39096      * Returns the rendering (formatting) function defined for the column.
39097      * @param {Number} col The column index.
39098      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39099      */
39100     getRenderer : function(col){
39101         if(!this.config[col].renderer){
39102             return Roo.grid.ColumnModel.defaultRenderer;
39103         }
39104         return this.config[col].renderer;
39105     },
39106
39107     /**
39108      * Sets the rendering (formatting) function for a column.
39109      * @param {Number} col The column index
39110      * @param {Function} fn The function to use to process the cell's raw data
39111      * to return HTML markup for the grid view. The render function is called with
39112      * the following parameters:<ul>
39113      * <li>Data value.</li>
39114      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39115      * <li>css A CSS style string to apply to the table cell.</li>
39116      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39117      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39118      * <li>Row index</li>
39119      * <li>Column index</li>
39120      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39121      */
39122     setRenderer : function(col, fn){
39123         this.config[col].renderer = fn;
39124     },
39125
39126     /**
39127      * Returns the width for the specified column.
39128      * @param {Number} col The column index
39129      * @return {Number}
39130      */
39131     getColumnWidth : function(col){
39132         return this.config[col].width * 1 || this.defaultWidth;
39133     },
39134
39135     /**
39136      * Sets the width for a column.
39137      * @param {Number} col The column index
39138      * @param {Number} width The new width
39139      */
39140     setColumnWidth : function(col, width, suppressEvent){
39141         this.config[col].width = width;
39142         this.totalWidth = null;
39143         if(!suppressEvent){
39144              this.fireEvent("widthchange", this, col, width);
39145         }
39146     },
39147
39148     /**
39149      * Returns the total width of all columns.
39150      * @param {Boolean} includeHidden True to include hidden column widths
39151      * @return {Number}
39152      */
39153     getTotalWidth : function(includeHidden){
39154         if(!this.totalWidth){
39155             this.totalWidth = 0;
39156             for(var i = 0, len = this.config.length; i < len; i++){
39157                 if(includeHidden || !this.isHidden(i)){
39158                     this.totalWidth += this.getColumnWidth(i);
39159                 }
39160             }
39161         }
39162         return this.totalWidth;
39163     },
39164
39165     /**
39166      * Returns the header for the specified column.
39167      * @param {Number} col The column index
39168      * @return {String}
39169      */
39170     getColumnHeader : function(col){
39171         return this.config[col].header;
39172     },
39173
39174     /**
39175      * Sets the header for a column.
39176      * @param {Number} col The column index
39177      * @param {String} header The new header
39178      */
39179     setColumnHeader : function(col, header){
39180         this.config[col].header = header;
39181         this.fireEvent("headerchange", this, col, header);
39182     },
39183
39184     /**
39185      * Returns the tooltip for the specified column.
39186      * @param {Number} col The column index
39187      * @return {String}
39188      */
39189     getColumnTooltip : function(col){
39190             return this.config[col].tooltip;
39191     },
39192     /**
39193      * Sets the tooltip for a column.
39194      * @param {Number} col The column index
39195      * @param {String} tooltip The new tooltip
39196      */
39197     setColumnTooltip : function(col, tooltip){
39198             this.config[col].tooltip = tooltip;
39199     },
39200
39201     /**
39202      * Returns the dataIndex for the specified column.
39203      * @param {Number} col The column index
39204      * @return {Number}
39205      */
39206     getDataIndex : function(col){
39207         return this.config[col].dataIndex;
39208     },
39209
39210     /**
39211      * Sets the dataIndex for a column.
39212      * @param {Number} col The column index
39213      * @param {Number} dataIndex The new dataIndex
39214      */
39215     setDataIndex : function(col, dataIndex){
39216         this.config[col].dataIndex = dataIndex;
39217     },
39218
39219     
39220     
39221     /**
39222      * Returns true if the cell is editable.
39223      * @param {Number} colIndex The column index
39224      * @param {Number} rowIndex The row index
39225      * @return {Boolean}
39226      */
39227     isCellEditable : function(colIndex, rowIndex){
39228         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39229     },
39230
39231     /**
39232      * Returns the editor defined for the cell/column.
39233      * return false or null to disable editing.
39234      * @param {Number} colIndex The column index
39235      * @param {Number} rowIndex The row index
39236      * @return {Object}
39237      */
39238     getCellEditor : function(colIndex, rowIndex){
39239         return this.config[colIndex].editor;
39240     },
39241
39242     /**
39243      * Sets if a column is editable.
39244      * @param {Number} col The column index
39245      * @param {Boolean} editable True if the column is editable
39246      */
39247     setEditable : function(col, editable){
39248         this.config[col].editable = editable;
39249     },
39250
39251
39252     /**
39253      * Returns true if the column is hidden.
39254      * @param {Number} colIndex The column index
39255      * @return {Boolean}
39256      */
39257     isHidden : function(colIndex){
39258         return this.config[colIndex].hidden;
39259     },
39260
39261
39262     /**
39263      * Returns true if the column width cannot be changed
39264      */
39265     isFixed : function(colIndex){
39266         return this.config[colIndex].fixed;
39267     },
39268
39269     /**
39270      * Returns true if the column can be resized
39271      * @return {Boolean}
39272      */
39273     isResizable : function(colIndex){
39274         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39275     },
39276     /**
39277      * Sets if a column is hidden.
39278      * @param {Number} colIndex The column index
39279      * @param {Boolean} hidden True if the column is hidden
39280      */
39281     setHidden : function(colIndex, hidden){
39282         this.config[colIndex].hidden = hidden;
39283         this.totalWidth = null;
39284         this.fireEvent("hiddenchange", this, colIndex, hidden);
39285     },
39286
39287     /**
39288      * Sets the editor for a column.
39289      * @param {Number} col The column index
39290      * @param {Object} editor The editor object
39291      */
39292     setEditor : function(col, editor){
39293         this.config[col].editor = editor;
39294     }
39295 });
39296
39297 Roo.grid.ColumnModel.defaultRenderer = function(value){
39298         if(typeof value == "string" && value.length < 1){
39299             return "&#160;";
39300         }
39301         return value;
39302 };
39303
39304 // Alias for backwards compatibility
39305 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39306 /*
39307  * Based on:
39308  * Ext JS Library 1.1.1
39309  * Copyright(c) 2006-2007, Ext JS, LLC.
39310  *
39311  * Originally Released Under LGPL - original licence link has changed is not relivant.
39312  *
39313  * Fork - LGPL
39314  * <script type="text/javascript">
39315  */
39316
39317 /**
39318  * @class Roo.grid.AbstractSelectionModel
39319  * @extends Roo.util.Observable
39320  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39321  * implemented by descendant classes.  This class should not be directly instantiated.
39322  * @constructor
39323  */
39324 Roo.grid.AbstractSelectionModel = function(){
39325     this.locked = false;
39326     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39327 };
39328
39329 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39330     /** @ignore Called by the grid automatically. Do not call directly. */
39331     init : function(grid){
39332         this.grid = grid;
39333         this.initEvents();
39334     },
39335
39336     /**
39337      * Locks the selections.
39338      */
39339     lock : function(){
39340         this.locked = true;
39341     },
39342
39343     /**
39344      * Unlocks the selections.
39345      */
39346     unlock : function(){
39347         this.locked = false;
39348     },
39349
39350     /**
39351      * Returns true if the selections are locked.
39352      * @return {Boolean}
39353      */
39354     isLocked : function(){
39355         return this.locked;
39356     }
39357 });/*
39358  * Based on:
39359  * Ext JS Library 1.1.1
39360  * Copyright(c) 2006-2007, Ext JS, LLC.
39361  *
39362  * Originally Released Under LGPL - original licence link has changed is not relivant.
39363  *
39364  * Fork - LGPL
39365  * <script type="text/javascript">
39366  */
39367 /**
39368  * @extends Roo.grid.AbstractSelectionModel
39369  * @class Roo.grid.RowSelectionModel
39370  * The default SelectionModel used by {@link Roo.grid.Grid}.
39371  * It supports multiple selections and keyboard selection/navigation. 
39372  * @constructor
39373  * @param {Object} config
39374  */
39375 Roo.grid.RowSelectionModel = function(config){
39376     Roo.apply(this, config);
39377     this.selections = new Roo.util.MixedCollection(false, function(o){
39378         return o.id;
39379     });
39380
39381     this.last = false;
39382     this.lastActive = false;
39383
39384     this.addEvents({
39385         /**
39386              * @event selectionchange
39387              * Fires when the selection changes
39388              * @param {SelectionModel} this
39389              */
39390             "selectionchange" : true,
39391         /**
39392              * @event afterselectionchange
39393              * Fires after the selection changes (eg. by key press or clicking)
39394              * @param {SelectionModel} this
39395              */
39396             "afterselectionchange" : true,
39397         /**
39398              * @event beforerowselect
39399              * Fires when a row is selected being selected, return false to cancel.
39400              * @param {SelectionModel} this
39401              * @param {Number} rowIndex The selected index
39402              * @param {Boolean} keepExisting False if other selections will be cleared
39403              */
39404             "beforerowselect" : true,
39405         /**
39406              * @event rowselect
39407              * Fires when a row is selected.
39408              * @param {SelectionModel} this
39409              * @param {Number} rowIndex The selected index
39410              * @param {Roo.data.Record} r The record
39411              */
39412             "rowselect" : true,
39413         /**
39414              * @event rowdeselect
39415              * Fires when a row is deselected.
39416              * @param {SelectionModel} this
39417              * @param {Number} rowIndex The selected index
39418              */
39419         "rowdeselect" : true
39420     });
39421     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39422     this.locked = false;
39423 };
39424
39425 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39426     /**
39427      * @cfg {Boolean} singleSelect
39428      * True to allow selection of only one row at a time (defaults to false)
39429      */
39430     singleSelect : false,
39431
39432     // private
39433     initEvents : function(){
39434
39435         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39436             this.grid.on("mousedown", this.handleMouseDown, this);
39437         }else{ // allow click to work like normal
39438             this.grid.on("rowclick", this.handleDragableRowClick, this);
39439         }
39440
39441         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39442             "up" : function(e){
39443                 if(!e.shiftKey){
39444                     this.selectPrevious(e.shiftKey);
39445                 }else if(this.last !== false && this.lastActive !== false){
39446                     var last = this.last;
39447                     this.selectRange(this.last,  this.lastActive-1);
39448                     this.grid.getView().focusRow(this.lastActive);
39449                     if(last !== false){
39450                         this.last = last;
39451                     }
39452                 }else{
39453                     this.selectFirstRow();
39454                 }
39455                 this.fireEvent("afterselectionchange", this);
39456             },
39457             "down" : function(e){
39458                 if(!e.shiftKey){
39459                     this.selectNext(e.shiftKey);
39460                 }else if(this.last !== false && this.lastActive !== false){
39461                     var last = this.last;
39462                     this.selectRange(this.last,  this.lastActive+1);
39463                     this.grid.getView().focusRow(this.lastActive);
39464                     if(last !== false){
39465                         this.last = last;
39466                     }
39467                 }else{
39468                     this.selectFirstRow();
39469                 }
39470                 this.fireEvent("afterselectionchange", this);
39471             },
39472             scope: this
39473         });
39474
39475         var view = this.grid.view;
39476         view.on("refresh", this.onRefresh, this);
39477         view.on("rowupdated", this.onRowUpdated, this);
39478         view.on("rowremoved", this.onRemove, this);
39479     },
39480
39481     // private
39482     onRefresh : function(){
39483         var ds = this.grid.dataSource, i, v = this.grid.view;
39484         var s = this.selections;
39485         s.each(function(r){
39486             if((i = ds.indexOfId(r.id)) != -1){
39487                 v.onRowSelect(i);
39488                 s.add(ds.getAt(i)); // updating the selection relate data
39489             }else{
39490                 s.remove(r);
39491             }
39492         });
39493     },
39494
39495     // private
39496     onRemove : function(v, index, r){
39497         this.selections.remove(r);
39498     },
39499
39500     // private
39501     onRowUpdated : function(v, index, r){
39502         if(this.isSelected(r)){
39503             v.onRowSelect(index);
39504         }
39505     },
39506
39507     /**
39508      * Select records.
39509      * @param {Array} records The records to select
39510      * @param {Boolean} keepExisting (optional) True to keep existing selections
39511      */
39512     selectRecords : function(records, keepExisting){
39513         if(!keepExisting){
39514             this.clearSelections();
39515         }
39516         var ds = this.grid.dataSource;
39517         for(var i = 0, len = records.length; i < len; i++){
39518             this.selectRow(ds.indexOf(records[i]), true);
39519         }
39520     },
39521
39522     /**
39523      * Gets the number of selected rows.
39524      * @return {Number}
39525      */
39526     getCount : function(){
39527         return this.selections.length;
39528     },
39529
39530     /**
39531      * Selects the first row in the grid.
39532      */
39533     selectFirstRow : function(){
39534         this.selectRow(0);
39535     },
39536
39537     /**
39538      * Select the last row.
39539      * @param {Boolean} keepExisting (optional) True to keep existing selections
39540      */
39541     selectLastRow : function(keepExisting){
39542         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39543     },
39544
39545     /**
39546      * Selects the row immediately following the last selected row.
39547      * @param {Boolean} keepExisting (optional) True to keep existing selections
39548      */
39549     selectNext : function(keepExisting){
39550         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39551             this.selectRow(this.last+1, keepExisting);
39552             this.grid.getView().focusRow(this.last);
39553         }
39554     },
39555
39556     /**
39557      * Selects the row that precedes the last selected row.
39558      * @param {Boolean} keepExisting (optional) True to keep existing selections
39559      */
39560     selectPrevious : function(keepExisting){
39561         if(this.last){
39562             this.selectRow(this.last-1, keepExisting);
39563             this.grid.getView().focusRow(this.last);
39564         }
39565     },
39566
39567     /**
39568      * Returns the selected records
39569      * @return {Array} Array of selected records
39570      */
39571     getSelections : function(){
39572         return [].concat(this.selections.items);
39573     },
39574
39575     /**
39576      * Returns the first selected record.
39577      * @return {Record}
39578      */
39579     getSelected : function(){
39580         return this.selections.itemAt(0);
39581     },
39582
39583
39584     /**
39585      * Clears all selections.
39586      */
39587     clearSelections : function(fast){
39588         if(this.locked) return;
39589         if(fast !== true){
39590             var ds = this.grid.dataSource;
39591             var s = this.selections;
39592             s.each(function(r){
39593                 this.deselectRow(ds.indexOfId(r.id));
39594             }, this);
39595             s.clear();
39596         }else{
39597             this.selections.clear();
39598         }
39599         this.last = false;
39600     },
39601
39602
39603     /**
39604      * Selects all rows.
39605      */
39606     selectAll : function(){
39607         if(this.locked) return;
39608         this.selections.clear();
39609         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39610             this.selectRow(i, true);
39611         }
39612     },
39613
39614     /**
39615      * Returns True if there is a selection.
39616      * @return {Boolean}
39617      */
39618     hasSelection : function(){
39619         return this.selections.length > 0;
39620     },
39621
39622     /**
39623      * Returns True if the specified row is selected.
39624      * @param {Number/Record} record The record or index of the record to check
39625      * @return {Boolean}
39626      */
39627     isSelected : function(index){
39628         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39629         return (r && this.selections.key(r.id) ? true : false);
39630     },
39631
39632     /**
39633      * Returns True if the specified record id is selected.
39634      * @param {String} id The id of record to check
39635      * @return {Boolean}
39636      */
39637     isIdSelected : function(id){
39638         return (this.selections.key(id) ? true : false);
39639     },
39640
39641     // private
39642     handleMouseDown : function(e, t){
39643         var view = this.grid.getView(), rowIndex;
39644         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39645             return;
39646         };
39647         if(e.shiftKey && this.last !== false){
39648             var last = this.last;
39649             this.selectRange(last, rowIndex, e.ctrlKey);
39650             this.last = last; // reset the last
39651             view.focusRow(rowIndex);
39652         }else{
39653             var isSelected = this.isSelected(rowIndex);
39654             if(e.button !== 0 && isSelected){
39655                 view.focusRow(rowIndex);
39656             }else if(e.ctrlKey && isSelected){
39657                 this.deselectRow(rowIndex);
39658             }else if(!isSelected){
39659                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39660                 view.focusRow(rowIndex);
39661             }
39662         }
39663         this.fireEvent("afterselectionchange", this);
39664     },
39665     // private
39666     handleDragableRowClick :  function(grid, rowIndex, e) 
39667     {
39668         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39669             this.selectRow(rowIndex, false);
39670             grid.view.focusRow(rowIndex);
39671              this.fireEvent("afterselectionchange", this);
39672         }
39673     },
39674     
39675     /**
39676      * Selects multiple rows.
39677      * @param {Array} rows Array of the indexes of the row to select
39678      * @param {Boolean} keepExisting (optional) True to keep existing selections
39679      */
39680     selectRows : function(rows, keepExisting){
39681         if(!keepExisting){
39682             this.clearSelections();
39683         }
39684         for(var i = 0, len = rows.length; i < len; i++){
39685             this.selectRow(rows[i], true);
39686         }
39687     },
39688
39689     /**
39690      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39691      * @param {Number} startRow The index of the first row in the range
39692      * @param {Number} endRow The index of the last row in the range
39693      * @param {Boolean} keepExisting (optional) True to retain existing selections
39694      */
39695     selectRange : function(startRow, endRow, keepExisting){
39696         if(this.locked) return;
39697         if(!keepExisting){
39698             this.clearSelections();
39699         }
39700         if(startRow <= endRow){
39701             for(var i = startRow; i <= endRow; i++){
39702                 this.selectRow(i, true);
39703             }
39704         }else{
39705             for(var i = startRow; i >= endRow; i--){
39706                 this.selectRow(i, true);
39707             }
39708         }
39709     },
39710
39711     /**
39712      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39713      * @param {Number} startRow The index of the first row in the range
39714      * @param {Number} endRow The index of the last row in the range
39715      */
39716     deselectRange : function(startRow, endRow, preventViewNotify){
39717         if(this.locked) return;
39718         for(var i = startRow; i <= endRow; i++){
39719             this.deselectRow(i, preventViewNotify);
39720         }
39721     },
39722
39723     /**
39724      * Selects a row.
39725      * @param {Number} row The index of the row to select
39726      * @param {Boolean} keepExisting (optional) True to keep existing selections
39727      */
39728     selectRow : function(index, keepExisting, preventViewNotify){
39729         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39730         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39731             if(!keepExisting || this.singleSelect){
39732                 this.clearSelections();
39733             }
39734             var r = this.grid.dataSource.getAt(index);
39735             this.selections.add(r);
39736             this.last = this.lastActive = index;
39737             if(!preventViewNotify){
39738                 this.grid.getView().onRowSelect(index);
39739             }
39740             this.fireEvent("rowselect", this, index, r);
39741             this.fireEvent("selectionchange", this);
39742         }
39743     },
39744
39745     /**
39746      * Deselects a row.
39747      * @param {Number} row The index of the row to deselect
39748      */
39749     deselectRow : function(index, preventViewNotify){
39750         if(this.locked) return;
39751         if(this.last == index){
39752             this.last = false;
39753         }
39754         if(this.lastActive == index){
39755             this.lastActive = false;
39756         }
39757         var r = this.grid.dataSource.getAt(index);
39758         this.selections.remove(r);
39759         if(!preventViewNotify){
39760             this.grid.getView().onRowDeselect(index);
39761         }
39762         this.fireEvent("rowdeselect", this, index);
39763         this.fireEvent("selectionchange", this);
39764     },
39765
39766     // private
39767     restoreLast : function(){
39768         if(this._last){
39769             this.last = this._last;
39770         }
39771     },
39772
39773     // private
39774     acceptsNav : function(row, col, cm){
39775         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39776     },
39777
39778     // private
39779     onEditorKey : function(field, e){
39780         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39781         if(k == e.TAB){
39782             e.stopEvent();
39783             ed.completeEdit();
39784             if(e.shiftKey){
39785                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39786             }else{
39787                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39788             }
39789         }else if(k == e.ENTER && !e.ctrlKey){
39790             e.stopEvent();
39791             ed.completeEdit();
39792             if(e.shiftKey){
39793                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39794             }else{
39795                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39796             }
39797         }else if(k == e.ESC){
39798             ed.cancelEdit();
39799         }
39800         if(newCell){
39801             g.startEditing(newCell[0], newCell[1]);
39802         }
39803     }
39804 });/*
39805  * Based on:
39806  * Ext JS Library 1.1.1
39807  * Copyright(c) 2006-2007, Ext JS, LLC.
39808  *
39809  * Originally Released Under LGPL - original licence link has changed is not relivant.
39810  *
39811  * Fork - LGPL
39812  * <script type="text/javascript">
39813  */
39814 /**
39815  * @class Roo.grid.CellSelectionModel
39816  * @extends Roo.grid.AbstractSelectionModel
39817  * This class provides the basic implementation for cell selection in a grid.
39818  * @constructor
39819  * @param {Object} config The object containing the configuration of this model.
39820  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39821  */
39822 Roo.grid.CellSelectionModel = function(config){
39823     Roo.apply(this, config);
39824
39825     this.selection = null;
39826
39827     this.addEvents({
39828         /**
39829              * @event beforerowselect
39830              * Fires before a cell is selected.
39831              * @param {SelectionModel} this
39832              * @param {Number} rowIndex The selected row index
39833              * @param {Number} colIndex The selected cell index
39834              */
39835             "beforecellselect" : true,
39836         /**
39837              * @event cellselect
39838              * Fires when a cell is selected.
39839              * @param {SelectionModel} this
39840              * @param {Number} rowIndex The selected row index
39841              * @param {Number} colIndex The selected cell index
39842              */
39843             "cellselect" : true,
39844         /**
39845              * @event selectionchange
39846              * Fires when the active selection changes.
39847              * @param {SelectionModel} this
39848              * @param {Object} selection null for no selection or an object (o) with two properties
39849                 <ul>
39850                 <li>o.record: the record object for the row the selection is in</li>
39851                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39852                 </ul>
39853              */
39854             "selectionchange" : true,
39855         /**
39856              * @event tabend
39857              * Fires when the tab (or enter) was pressed on the last editable cell
39858              * You can use this to trigger add new row.
39859              * @param {SelectionModel} this
39860              */
39861             "tabend" : true,
39862          /**
39863              * @event beforeeditnext
39864              * Fires before the next editable sell is made active
39865              * You can use this to skip to another cell or fire the tabend
39866              *    if you set cell to false
39867              * @param {Object} eventdata object : { cell : [ row, col ] } 
39868              */
39869             "beforeeditnext" : true
39870     });
39871     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39872 };
39873
39874 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39875     
39876     enter_is_tab: false,
39877
39878     /** @ignore */
39879     initEvents : function(){
39880         this.grid.on("mousedown", this.handleMouseDown, this);
39881         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39882         var view = this.grid.view;
39883         view.on("refresh", this.onViewChange, this);
39884         view.on("rowupdated", this.onRowUpdated, this);
39885         view.on("beforerowremoved", this.clearSelections, this);
39886         view.on("beforerowsinserted", this.clearSelections, this);
39887         if(this.grid.isEditor){
39888             this.grid.on("beforeedit", this.beforeEdit,  this);
39889         }
39890     },
39891
39892         //private
39893     beforeEdit : function(e){
39894         this.select(e.row, e.column, false, true, e.record);
39895     },
39896
39897         //private
39898     onRowUpdated : function(v, index, r){
39899         if(this.selection && this.selection.record == r){
39900             v.onCellSelect(index, this.selection.cell[1]);
39901         }
39902     },
39903
39904         //private
39905     onViewChange : function(){
39906         this.clearSelections(true);
39907     },
39908
39909         /**
39910          * Returns the currently selected cell,.
39911          * @return {Array} The selected cell (row, column) or null if none selected.
39912          */
39913     getSelectedCell : function(){
39914         return this.selection ? this.selection.cell : null;
39915     },
39916
39917     /**
39918      * Clears all selections.
39919      * @param {Boolean} true to prevent the gridview from being notified about the change.
39920      */
39921     clearSelections : function(preventNotify){
39922         var s = this.selection;
39923         if(s){
39924             if(preventNotify !== true){
39925                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39926             }
39927             this.selection = null;
39928             this.fireEvent("selectionchange", this, null);
39929         }
39930     },
39931
39932     /**
39933      * Returns true if there is a selection.
39934      * @return {Boolean}
39935      */
39936     hasSelection : function(){
39937         return this.selection ? true : false;
39938     },
39939
39940     /** @ignore */
39941     handleMouseDown : function(e, t){
39942         var v = this.grid.getView();
39943         if(this.isLocked()){
39944             return;
39945         };
39946         var row = v.findRowIndex(t);
39947         var cell = v.findCellIndex(t);
39948         if(row !== false && cell !== false){
39949             this.select(row, cell);
39950         }
39951     },
39952
39953     /**
39954      * Selects a cell.
39955      * @param {Number} rowIndex
39956      * @param {Number} collIndex
39957      */
39958     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39959         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39960             this.clearSelections();
39961             r = r || this.grid.dataSource.getAt(rowIndex);
39962             this.selection = {
39963                 record : r,
39964                 cell : [rowIndex, colIndex]
39965             };
39966             if(!preventViewNotify){
39967                 var v = this.grid.getView();
39968                 v.onCellSelect(rowIndex, colIndex);
39969                 if(preventFocus !== true){
39970                     v.focusCell(rowIndex, colIndex);
39971                 }
39972             }
39973             this.fireEvent("cellselect", this, rowIndex, colIndex);
39974             this.fireEvent("selectionchange", this, this.selection);
39975         }
39976     },
39977
39978         //private
39979     isSelectable : function(rowIndex, colIndex, cm){
39980         return !cm.isHidden(colIndex);
39981     },
39982
39983     /** @ignore */
39984     handleKeyDown : function(e){
39985         //Roo.log('Cell Sel Model handleKeyDown');
39986         if(!e.isNavKeyPress()){
39987             return;
39988         }
39989         var g = this.grid, s = this.selection;
39990         if(!s){
39991             e.stopEvent();
39992             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39993             if(cell){
39994                 this.select(cell[0], cell[1]);
39995             }
39996             return;
39997         }
39998         var sm = this;
39999         var walk = function(row, col, step){
40000             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40001         };
40002         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40003         var newCell;
40004
40005       
40006
40007         switch(k){
40008             case e.TAB:
40009                 // handled by onEditorKey
40010                 if (g.isEditor && g.editing) {
40011                     return;
40012                 }
40013                 if(e.shiftKey) {
40014                     newCell = walk(r, c-1, -1);
40015                 } else {
40016                     newCell = walk(r, c+1, 1);
40017                 }
40018                 break;
40019             
40020             case e.DOWN:
40021                newCell = walk(r+1, c, 1);
40022                 break;
40023             
40024             case e.UP:
40025                 newCell = walk(r-1, c, -1);
40026                 break;
40027             
40028             case e.RIGHT:
40029                 newCell = walk(r, c+1, 1);
40030                 break;
40031             
40032             case e.LEFT:
40033                 newCell = walk(r, c-1, -1);
40034                 break;
40035             
40036             case e.ENTER:
40037                 
40038                 if(g.isEditor && !g.editing){
40039                    g.startEditing(r, c);
40040                    e.stopEvent();
40041                    return;
40042                 }
40043                 
40044                 
40045              break;
40046         };
40047         if(newCell){
40048             this.select(newCell[0], newCell[1]);
40049             e.stopEvent();
40050             
40051         }
40052     },
40053
40054     acceptsNav : function(row, col, cm){
40055         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40056     },
40057     /**
40058      * Selects a cell.
40059      * @param {Number} field (not used) - as it's normally used as a listener
40060      * @param {Number} e - event - fake it by using
40061      *
40062      * var e = Roo.EventObjectImpl.prototype;
40063      * e.keyCode = e.TAB
40064      *
40065      * 
40066      */
40067     onEditorKey : function(field, e){
40068         
40069         var k = e.getKey(),
40070             newCell,
40071             g = this.grid,
40072             ed = g.activeEditor,
40073             forward = false;
40074         ///Roo.log('onEditorKey' + k);
40075         
40076         
40077         if (this.enter_is_tab && k == e.ENTER) {
40078             k = e.TAB;
40079         }
40080         
40081         if(k == e.TAB){
40082             if(e.shiftKey){
40083                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40084             }else{
40085                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40086                 forward = true;
40087             }
40088             
40089             e.stopEvent();
40090             
40091         } else if(k == e.ENTER &&  !e.ctrlKey){
40092             ed.completeEdit();
40093             e.stopEvent();
40094             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40095         
40096                 } else if(k == e.ESC){
40097             ed.cancelEdit();
40098         }
40099                 
40100         if (newCell) {
40101             var ecall = { cell : newCell, forward : forward };
40102             this.fireEvent('beforeeditnext', ecall );
40103             newCell = ecall.cell;
40104                         forward = ecall.forward;
40105         }
40106                 
40107         if(newCell){
40108             //Roo.log('next cell after edit');
40109             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40110         } else if (forward) {
40111             // tabbed past last
40112             this.fireEvent.defer(100, this, ['tabend',this]);
40113         }
40114     }
40115 });/*
40116  * Based on:
40117  * Ext JS Library 1.1.1
40118  * Copyright(c) 2006-2007, Ext JS, LLC.
40119  *
40120  * Originally Released Under LGPL - original licence link has changed is not relivant.
40121  *
40122  * Fork - LGPL
40123  * <script type="text/javascript">
40124  */
40125  
40126 /**
40127  * @class Roo.grid.EditorGrid
40128  * @extends Roo.grid.Grid
40129  * Class for creating and editable grid.
40130  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40131  * The container MUST have some type of size defined for the grid to fill. The container will be 
40132  * automatically set to position relative if it isn't already.
40133  * @param {Object} dataSource The data model to bind to
40134  * @param {Object} colModel The column model with info about this grid's columns
40135  */
40136 Roo.grid.EditorGrid = function(container, config){
40137     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40138     this.getGridEl().addClass("xedit-grid");
40139
40140     if(!this.selModel){
40141         this.selModel = new Roo.grid.CellSelectionModel();
40142     }
40143
40144     this.activeEditor = null;
40145
40146         this.addEvents({
40147             /**
40148              * @event beforeedit
40149              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40150              * <ul style="padding:5px;padding-left:16px;">
40151              * <li>grid - This grid</li>
40152              * <li>record - The record being edited</li>
40153              * <li>field - The field name being edited</li>
40154              * <li>value - The value for the field being edited.</li>
40155              * <li>row - The grid row index</li>
40156              * <li>column - The grid column index</li>
40157              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40158              * </ul>
40159              * @param {Object} e An edit event (see above for description)
40160              */
40161             "beforeedit" : true,
40162             /**
40163              * @event afteredit
40164              * Fires after a cell is edited. <br />
40165              * <ul style="padding:5px;padding-left:16px;">
40166              * <li>grid - This grid</li>
40167              * <li>record - The record being edited</li>
40168              * <li>field - The field name being edited</li>
40169              * <li>value - The value being set</li>
40170              * <li>originalValue - The original value for the field, before the edit.</li>
40171              * <li>row - The grid row index</li>
40172              * <li>column - The grid column index</li>
40173              * </ul>
40174              * @param {Object} e An edit event (see above for description)
40175              */
40176             "afteredit" : true,
40177             /**
40178              * @event validateedit
40179              * Fires after a cell is edited, but before the value is set in the record. 
40180          * You can use this to modify the value being set in the field, Return false
40181              * to cancel the change. The edit event object has the following properties <br />
40182              * <ul style="padding:5px;padding-left:16px;">
40183          * <li>editor - This editor</li>
40184              * <li>grid - This grid</li>
40185              * <li>record - The record being edited</li>
40186              * <li>field - The field name being edited</li>
40187              * <li>value - The value being set</li>
40188              * <li>originalValue - The original value for the field, before the edit.</li>
40189              * <li>row - The grid row index</li>
40190              * <li>column - The grid column index</li>
40191              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40192              * </ul>
40193              * @param {Object} e An edit event (see above for description)
40194              */
40195             "validateedit" : true
40196         });
40197     this.on("bodyscroll", this.stopEditing,  this);
40198     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40199 };
40200
40201 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40202     /**
40203      * @cfg {Number} clicksToEdit
40204      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40205      */
40206     clicksToEdit: 2,
40207
40208     // private
40209     isEditor : true,
40210     // private
40211     trackMouseOver: false, // causes very odd FF errors
40212
40213     onCellDblClick : function(g, row, col){
40214         this.startEditing(row, col);
40215     },
40216
40217     onEditComplete : function(ed, value, startValue){
40218         this.editing = false;
40219         this.activeEditor = null;
40220         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40221         var r = ed.record;
40222         var field = this.colModel.getDataIndex(ed.col);
40223         var e = {
40224             grid: this,
40225             record: r,
40226             field: field,
40227             originalValue: startValue,
40228             value: value,
40229             row: ed.row,
40230             column: ed.col,
40231             cancel:false,
40232             editor: ed
40233         };
40234         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
40235         cell.show();
40236           
40237         if(String(value) !== String(startValue)){
40238             
40239             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40240                 r.set(field, e.value);
40241                 // if we are dealing with a combo box..
40242                 // then we also set the 'name' colum to be the displayField
40243                 if (ed.field.displayField && ed.field.name) {
40244                     r.set(ed.field.name, ed.field.el.dom.value);
40245                 }
40246                 
40247                 delete e.cancel; //?? why!!!
40248                 this.fireEvent("afteredit", e);
40249             }
40250         } else {
40251             this.fireEvent("afteredit", e); // always fire it!
40252         }
40253         this.view.focusCell(ed.row, ed.col);
40254     },
40255
40256     /**
40257      * Starts editing the specified for the specified row/column
40258      * @param {Number} rowIndex
40259      * @param {Number} colIndex
40260      */
40261     startEditing : function(row, col){
40262         this.stopEditing();
40263         if(this.colModel.isCellEditable(col, row)){
40264             this.view.ensureVisible(row, col, true);
40265           
40266             var r = this.dataSource.getAt(row);
40267             var field = this.colModel.getDataIndex(col);
40268             var cell = Roo.get(this.view.getCell(row,col));
40269             var e = {
40270                 grid: this,
40271                 record: r,
40272                 field: field,
40273                 value: r.data[field],
40274                 row: row,
40275                 column: col,
40276                 cancel:false 
40277             };
40278             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40279                 this.editing = true;
40280                 var ed = this.colModel.getCellEditor(col, row);
40281                 
40282                 if (!ed) {
40283                     return;
40284                 }
40285                 if(!ed.rendered){
40286                     ed.render(ed.parentEl || document.body);
40287                 }
40288                 ed.field.reset();
40289                
40290                 cell.hide();
40291                 
40292                 (function(){ // complex but required for focus issues in safari, ie and opera
40293                     ed.row = row;
40294                     ed.col = col;
40295                     ed.record = r;
40296                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40297                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40298                     this.activeEditor = ed;
40299                     var v = r.data[field];
40300                     ed.startEdit(this.view.getCell(row, col), v);
40301                     // combo's with 'displayField and name set
40302                     if (ed.field.displayField && ed.field.name) {
40303                         ed.field.el.dom.value = r.data[ed.field.name];
40304                     }
40305                     
40306                     
40307                 }).defer(50, this);
40308             }
40309         }
40310     },
40311         
40312     /**
40313      * Stops any active editing
40314      */
40315     stopEditing : function(){
40316         if(this.activeEditor){
40317             this.activeEditor.completeEdit();
40318         }
40319         this.activeEditor = null;
40320     },
40321         
40322          /**
40323      * Called to get grid's drag proxy text, by default returns this.ddText.
40324      * @return {String}
40325      */
40326     getDragDropText : function(){
40327         var count = this.selModel.getSelectedCell() ? 1 : 0;
40328         return String.format(this.ddText, count, count == 1 ? '' : 's');
40329     }
40330         
40331 });/*
40332  * Based on:
40333  * Ext JS Library 1.1.1
40334  * Copyright(c) 2006-2007, Ext JS, LLC.
40335  *
40336  * Originally Released Under LGPL - original licence link has changed is not relivant.
40337  *
40338  * Fork - LGPL
40339  * <script type="text/javascript">
40340  */
40341
40342 // private - not really -- you end up using it !
40343 // This is a support class used internally by the Grid components
40344
40345 /**
40346  * @class Roo.grid.GridEditor
40347  * @extends Roo.Editor
40348  * Class for creating and editable grid elements.
40349  * @param {Object} config any settings (must include field)
40350  */
40351 Roo.grid.GridEditor = function(field, config){
40352     if (!config && field.field) {
40353         config = field;
40354         field = Roo.factory(config.field, Roo.form);
40355     }
40356     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40357     field.monitorTab = false;
40358 };
40359
40360 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40361     
40362     /**
40363      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40364      */
40365     
40366     alignment: "tl-tl",
40367     autoSize: "width",
40368     hideEl : false,
40369     cls: "x-small-editor x-grid-editor",
40370     shim:false,
40371     shadow:"frame"
40372 });/*
40373  * Based on:
40374  * Ext JS Library 1.1.1
40375  * Copyright(c) 2006-2007, Ext JS, LLC.
40376  *
40377  * Originally Released Under LGPL - original licence link has changed is not relivant.
40378  *
40379  * Fork - LGPL
40380  * <script type="text/javascript">
40381  */
40382   
40383
40384   
40385 Roo.grid.PropertyRecord = Roo.data.Record.create([
40386     {name:'name',type:'string'},  'value'
40387 ]);
40388
40389
40390 Roo.grid.PropertyStore = function(grid, source){
40391     this.grid = grid;
40392     this.store = new Roo.data.Store({
40393         recordType : Roo.grid.PropertyRecord
40394     });
40395     this.store.on('update', this.onUpdate,  this);
40396     if(source){
40397         this.setSource(source);
40398     }
40399     Roo.grid.PropertyStore.superclass.constructor.call(this);
40400 };
40401
40402
40403
40404 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40405     setSource : function(o){
40406         this.source = o;
40407         this.store.removeAll();
40408         var data = [];
40409         for(var k in o){
40410             if(this.isEditableValue(o[k])){
40411                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40412             }
40413         }
40414         this.store.loadRecords({records: data}, {}, true);
40415     },
40416
40417     onUpdate : function(ds, record, type){
40418         if(type == Roo.data.Record.EDIT){
40419             var v = record.data['value'];
40420             var oldValue = record.modified['value'];
40421             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40422                 this.source[record.id] = v;
40423                 record.commit();
40424                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40425             }else{
40426                 record.reject();
40427             }
40428         }
40429     },
40430
40431     getProperty : function(row){
40432        return this.store.getAt(row);
40433     },
40434
40435     isEditableValue: function(val){
40436         if(val && val instanceof Date){
40437             return true;
40438         }else if(typeof val == 'object' || typeof val == 'function'){
40439             return false;
40440         }
40441         return true;
40442     },
40443
40444     setValue : function(prop, value){
40445         this.source[prop] = value;
40446         this.store.getById(prop).set('value', value);
40447     },
40448
40449     getSource : function(){
40450         return this.source;
40451     }
40452 });
40453
40454 Roo.grid.PropertyColumnModel = function(grid, store){
40455     this.grid = grid;
40456     var g = Roo.grid;
40457     g.PropertyColumnModel.superclass.constructor.call(this, [
40458         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40459         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40460     ]);
40461     this.store = store;
40462     this.bselect = Roo.DomHelper.append(document.body, {
40463         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40464             {tag: 'option', value: 'true', html: 'true'},
40465             {tag: 'option', value: 'false', html: 'false'}
40466         ]
40467     });
40468     Roo.id(this.bselect);
40469     var f = Roo.form;
40470     this.editors = {
40471         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40472         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40473         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40474         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40475         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40476     };
40477     this.renderCellDelegate = this.renderCell.createDelegate(this);
40478     this.renderPropDelegate = this.renderProp.createDelegate(this);
40479 };
40480
40481 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40482     
40483     
40484     nameText : 'Name',
40485     valueText : 'Value',
40486     
40487     dateFormat : 'm/j/Y',
40488     
40489     
40490     renderDate : function(dateVal){
40491         return dateVal.dateFormat(this.dateFormat);
40492     },
40493
40494     renderBool : function(bVal){
40495         return bVal ? 'true' : 'false';
40496     },
40497
40498     isCellEditable : function(colIndex, rowIndex){
40499         return colIndex == 1;
40500     },
40501
40502     getRenderer : function(col){
40503         return col == 1 ?
40504             this.renderCellDelegate : this.renderPropDelegate;
40505     },
40506
40507     renderProp : function(v){
40508         return this.getPropertyName(v);
40509     },
40510
40511     renderCell : function(val){
40512         var rv = val;
40513         if(val instanceof Date){
40514             rv = this.renderDate(val);
40515         }else if(typeof val == 'boolean'){
40516             rv = this.renderBool(val);
40517         }
40518         return Roo.util.Format.htmlEncode(rv);
40519     },
40520
40521     getPropertyName : function(name){
40522         var pn = this.grid.propertyNames;
40523         return pn && pn[name] ? pn[name] : name;
40524     },
40525
40526     getCellEditor : function(colIndex, rowIndex){
40527         var p = this.store.getProperty(rowIndex);
40528         var n = p.data['name'], val = p.data['value'];
40529         
40530         if(typeof(this.grid.customEditors[n]) == 'string'){
40531             return this.editors[this.grid.customEditors[n]];
40532         }
40533         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40534             return this.grid.customEditors[n];
40535         }
40536         if(val instanceof Date){
40537             return this.editors['date'];
40538         }else if(typeof val == 'number'){
40539             return this.editors['number'];
40540         }else if(typeof val == 'boolean'){
40541             return this.editors['boolean'];
40542         }else{
40543             return this.editors['string'];
40544         }
40545     }
40546 });
40547
40548 /**
40549  * @class Roo.grid.PropertyGrid
40550  * @extends Roo.grid.EditorGrid
40551  * This class represents the  interface of a component based property grid control.
40552  * <br><br>Usage:<pre><code>
40553  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40554       
40555  });
40556  // set any options
40557  grid.render();
40558  * </code></pre>
40559   
40560  * @constructor
40561  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40562  * The container MUST have some type of size defined for the grid to fill. The container will be
40563  * automatically set to position relative if it isn't already.
40564  * @param {Object} config A config object that sets properties on this grid.
40565  */
40566 Roo.grid.PropertyGrid = function(container, config){
40567     config = config || {};
40568     var store = new Roo.grid.PropertyStore(this);
40569     this.store = store;
40570     var cm = new Roo.grid.PropertyColumnModel(this, store);
40571     store.store.sort('name', 'ASC');
40572     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40573         ds: store.store,
40574         cm: cm,
40575         enableColLock:false,
40576         enableColumnMove:false,
40577         stripeRows:false,
40578         trackMouseOver: false,
40579         clicksToEdit:1
40580     }, config));
40581     this.getGridEl().addClass('x-props-grid');
40582     this.lastEditRow = null;
40583     this.on('columnresize', this.onColumnResize, this);
40584     this.addEvents({
40585          /**
40586              * @event beforepropertychange
40587              * Fires before a property changes (return false to stop?)
40588              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40589              * @param {String} id Record Id
40590              * @param {String} newval New Value
40591          * @param {String} oldval Old Value
40592              */
40593         "beforepropertychange": true,
40594         /**
40595              * @event propertychange
40596              * Fires after a property changes
40597              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40598              * @param {String} id Record Id
40599              * @param {String} newval New Value
40600          * @param {String} oldval Old Value
40601              */
40602         "propertychange": true
40603     });
40604     this.customEditors = this.customEditors || {};
40605 };
40606 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40607     
40608      /**
40609      * @cfg {Object} customEditors map of colnames=> custom editors.
40610      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40611      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40612      * false disables editing of the field.
40613          */
40614     
40615       /**
40616      * @cfg {Object} propertyNames map of property Names to their displayed value
40617          */
40618     
40619     render : function(){
40620         Roo.grid.PropertyGrid.superclass.render.call(this);
40621         this.autoSize.defer(100, this);
40622     },
40623
40624     autoSize : function(){
40625         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40626         if(this.view){
40627             this.view.fitColumns();
40628         }
40629     },
40630
40631     onColumnResize : function(){
40632         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40633         this.autoSize();
40634     },
40635     /**
40636      * Sets the data for the Grid
40637      * accepts a Key => Value object of all the elements avaiable.
40638      * @param {Object} data  to appear in grid.
40639      */
40640     setSource : function(source){
40641         this.store.setSource(source);
40642         //this.autoSize();
40643     },
40644     /**
40645      * Gets all the data from the grid.
40646      * @return {Object} data  data stored in grid
40647      */
40648     getSource : function(){
40649         return this.store.getSource();
40650     }
40651 });/*
40652   
40653  * Licence LGPL
40654  
40655  */
40656  
40657 /**
40658  * @class Roo.grid.Calendar
40659  * @extends Roo.util.Grid
40660  * This class extends the Grid to provide a calendar widget
40661  * <br><br>Usage:<pre><code>
40662  var grid = new Roo.grid.Calendar("my-container-id", {
40663      ds: myDataStore,
40664      cm: myColModel,
40665      selModel: mySelectionModel,
40666      autoSizeColumns: true,
40667      monitorWindowResize: false,
40668      trackMouseOver: true
40669      eventstore : real data store..
40670  });
40671  // set any options
40672  grid.render();
40673   
40674   * @constructor
40675  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40676  * The container MUST have some type of size defined for the grid to fill. The container will be
40677  * automatically set to position relative if it isn't already.
40678  * @param {Object} config A config object that sets properties on this grid.
40679  */
40680 Roo.grid.Calendar = function(container, config){
40681         // initialize the container
40682         this.container = Roo.get(container);
40683         this.container.update("");
40684         this.container.setStyle("overflow", "hidden");
40685     this.container.addClass('x-grid-container');
40686
40687     this.id = this.container.id;
40688
40689     Roo.apply(this, config);
40690     // check and correct shorthanded configs
40691     
40692     var rows = [];
40693     var d =1;
40694     for (var r = 0;r < 6;r++) {
40695         
40696         rows[r]=[];
40697         for (var c =0;c < 7;c++) {
40698             rows[r][c]= '';
40699         }
40700     }
40701     if (this.eventStore) {
40702         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40703         this.eventStore.on('load',this.onLoad, this);
40704         this.eventStore.on('beforeload',this.clearEvents, this);
40705          
40706     }
40707     
40708     this.dataSource = new Roo.data.Store({
40709             proxy: new Roo.data.MemoryProxy(rows),
40710             reader: new Roo.data.ArrayReader({}, [
40711                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40712     });
40713
40714     this.dataSource.load();
40715     this.ds = this.dataSource;
40716     this.ds.xmodule = this.xmodule || false;
40717     
40718     
40719     var cellRender = function(v,x,r)
40720     {
40721         return String.format(
40722             '<div class="fc-day  fc-widget-content"><div>' +
40723                 '<div class="fc-event-container"></div>' +
40724                 '<div class="fc-day-number">{0}</div>'+
40725                 
40726                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40727             '</div></div>', v);
40728     
40729     }
40730     
40731     
40732     this.colModel = new Roo.grid.ColumnModel( [
40733         {
40734             xtype: 'ColumnModel',
40735             xns: Roo.grid,
40736             dataIndex : 'weekday0',
40737             header : 'Sunday',
40738             renderer : cellRender
40739         },
40740         {
40741             xtype: 'ColumnModel',
40742             xns: Roo.grid,
40743             dataIndex : 'weekday1',
40744             header : 'Monday',
40745             renderer : cellRender
40746         },
40747         {
40748             xtype: 'ColumnModel',
40749             xns: Roo.grid,
40750             dataIndex : 'weekday2',
40751             header : 'Tuesday',
40752             renderer : cellRender
40753         },
40754         {
40755             xtype: 'ColumnModel',
40756             xns: Roo.grid,
40757             dataIndex : 'weekday3',
40758             header : 'Wednesday',
40759             renderer : cellRender
40760         },
40761         {
40762             xtype: 'ColumnModel',
40763             xns: Roo.grid,
40764             dataIndex : 'weekday4',
40765             header : 'Thursday',
40766             renderer : cellRender
40767         },
40768         {
40769             xtype: 'ColumnModel',
40770             xns: Roo.grid,
40771             dataIndex : 'weekday5',
40772             header : 'Friday',
40773             renderer : cellRender
40774         },
40775         {
40776             xtype: 'ColumnModel',
40777             xns: Roo.grid,
40778             dataIndex : 'weekday6',
40779             header : 'Saturday',
40780             renderer : cellRender
40781         }
40782     ]);
40783     this.cm = this.colModel;
40784     this.cm.xmodule = this.xmodule || false;
40785  
40786         
40787           
40788     //this.selModel = new Roo.grid.CellSelectionModel();
40789     //this.sm = this.selModel;
40790     //this.selModel.init(this);
40791     
40792     
40793     if(this.width){
40794         this.container.setWidth(this.width);
40795     }
40796
40797     if(this.height){
40798         this.container.setHeight(this.height);
40799     }
40800     /** @private */
40801         this.addEvents({
40802         // raw events
40803         /**
40804          * @event click
40805          * The raw click event for the entire grid.
40806          * @param {Roo.EventObject} e
40807          */
40808         "click" : true,
40809         /**
40810          * @event dblclick
40811          * The raw dblclick event for the entire grid.
40812          * @param {Roo.EventObject} e
40813          */
40814         "dblclick" : true,
40815         /**
40816          * @event contextmenu
40817          * The raw contextmenu event for the entire grid.
40818          * @param {Roo.EventObject} e
40819          */
40820         "contextmenu" : true,
40821         /**
40822          * @event mousedown
40823          * The raw mousedown event for the entire grid.
40824          * @param {Roo.EventObject} e
40825          */
40826         "mousedown" : true,
40827         /**
40828          * @event mouseup
40829          * The raw mouseup event for the entire grid.
40830          * @param {Roo.EventObject} e
40831          */
40832         "mouseup" : true,
40833         /**
40834          * @event mouseover
40835          * The raw mouseover event for the entire grid.
40836          * @param {Roo.EventObject} e
40837          */
40838         "mouseover" : true,
40839         /**
40840          * @event mouseout
40841          * The raw mouseout event for the entire grid.
40842          * @param {Roo.EventObject} e
40843          */
40844         "mouseout" : true,
40845         /**
40846          * @event keypress
40847          * The raw keypress event for the entire grid.
40848          * @param {Roo.EventObject} e
40849          */
40850         "keypress" : true,
40851         /**
40852          * @event keydown
40853          * The raw keydown event for the entire grid.
40854          * @param {Roo.EventObject} e
40855          */
40856         "keydown" : true,
40857
40858         // custom events
40859
40860         /**
40861          * @event cellclick
40862          * Fires when a cell is clicked
40863          * @param {Grid} this
40864          * @param {Number} rowIndex
40865          * @param {Number} columnIndex
40866          * @param {Roo.EventObject} e
40867          */
40868         "cellclick" : true,
40869         /**
40870          * @event celldblclick
40871          * Fires when a cell is double clicked
40872          * @param {Grid} this
40873          * @param {Number} rowIndex
40874          * @param {Number} columnIndex
40875          * @param {Roo.EventObject} e
40876          */
40877         "celldblclick" : true,
40878         /**
40879          * @event rowclick
40880          * Fires when a row is clicked
40881          * @param {Grid} this
40882          * @param {Number} rowIndex
40883          * @param {Roo.EventObject} e
40884          */
40885         "rowclick" : true,
40886         /**
40887          * @event rowdblclick
40888          * Fires when a row is double clicked
40889          * @param {Grid} this
40890          * @param {Number} rowIndex
40891          * @param {Roo.EventObject} e
40892          */
40893         "rowdblclick" : true,
40894         /**
40895          * @event headerclick
40896          * Fires when a header is clicked
40897          * @param {Grid} this
40898          * @param {Number} columnIndex
40899          * @param {Roo.EventObject} e
40900          */
40901         "headerclick" : true,
40902         /**
40903          * @event headerdblclick
40904          * Fires when a header cell is double clicked
40905          * @param {Grid} this
40906          * @param {Number} columnIndex
40907          * @param {Roo.EventObject} e
40908          */
40909         "headerdblclick" : true,
40910         /**
40911          * @event rowcontextmenu
40912          * Fires when a row is right clicked
40913          * @param {Grid} this
40914          * @param {Number} rowIndex
40915          * @param {Roo.EventObject} e
40916          */
40917         "rowcontextmenu" : true,
40918         /**
40919          * @event cellcontextmenu
40920          * Fires when a cell is right clicked
40921          * @param {Grid} this
40922          * @param {Number} rowIndex
40923          * @param {Number} cellIndex
40924          * @param {Roo.EventObject} e
40925          */
40926          "cellcontextmenu" : true,
40927         /**
40928          * @event headercontextmenu
40929          * Fires when a header is right clicked
40930          * @param {Grid} this
40931          * @param {Number} columnIndex
40932          * @param {Roo.EventObject} e
40933          */
40934         "headercontextmenu" : true,
40935         /**
40936          * @event bodyscroll
40937          * Fires when the body element is scrolled
40938          * @param {Number} scrollLeft
40939          * @param {Number} scrollTop
40940          */
40941         "bodyscroll" : true,
40942         /**
40943          * @event columnresize
40944          * Fires when the user resizes a column
40945          * @param {Number} columnIndex
40946          * @param {Number} newSize
40947          */
40948         "columnresize" : true,
40949         /**
40950          * @event columnmove
40951          * Fires when the user moves a column
40952          * @param {Number} oldIndex
40953          * @param {Number} newIndex
40954          */
40955         "columnmove" : true,
40956         /**
40957          * @event startdrag
40958          * Fires when row(s) start being dragged
40959          * @param {Grid} this
40960          * @param {Roo.GridDD} dd The drag drop object
40961          * @param {event} e The raw browser event
40962          */
40963         "startdrag" : true,
40964         /**
40965          * @event enddrag
40966          * Fires when a drag operation is complete
40967          * @param {Grid} this
40968          * @param {Roo.GridDD} dd The drag drop object
40969          * @param {event} e The raw browser event
40970          */
40971         "enddrag" : true,
40972         /**
40973          * @event dragdrop
40974          * Fires when dragged row(s) are dropped on a valid DD target
40975          * @param {Grid} this
40976          * @param {Roo.GridDD} dd The drag drop object
40977          * @param {String} targetId The target drag drop object
40978          * @param {event} e The raw browser event
40979          */
40980         "dragdrop" : true,
40981         /**
40982          * @event dragover
40983          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40984          * @param {Grid} this
40985          * @param {Roo.GridDD} dd The drag drop object
40986          * @param {String} targetId The target drag drop object
40987          * @param {event} e The raw browser event
40988          */
40989         "dragover" : true,
40990         /**
40991          * @event dragenter
40992          *  Fires when the dragged row(s) first cross another DD target while being dragged
40993          * @param {Grid} this
40994          * @param {Roo.GridDD} dd The drag drop object
40995          * @param {String} targetId The target drag drop object
40996          * @param {event} e The raw browser event
40997          */
40998         "dragenter" : true,
40999         /**
41000          * @event dragout
41001          * Fires when the dragged row(s) leave another DD target while being dragged
41002          * @param {Grid} this
41003          * @param {Roo.GridDD} dd The drag drop object
41004          * @param {String} targetId The target drag drop object
41005          * @param {event} e The raw browser event
41006          */
41007         "dragout" : true,
41008         /**
41009          * @event rowclass
41010          * Fires when a row is rendered, so you can change add a style to it.
41011          * @param {GridView} gridview   The grid view
41012          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41013          */
41014         'rowclass' : true,
41015
41016         /**
41017          * @event render
41018          * Fires when the grid is rendered
41019          * @param {Grid} grid
41020          */
41021         'render' : true,
41022             /**
41023              * @event select
41024              * Fires when a date is selected
41025              * @param {DatePicker} this
41026              * @param {Date} date The selected date
41027              */
41028         'select': true,
41029         /**
41030              * @event monthchange
41031              * Fires when the displayed month changes 
41032              * @param {DatePicker} this
41033              * @param {Date} date The selected month
41034              */
41035         'monthchange': true,
41036         /**
41037              * @event evententer
41038              * Fires when mouse over an event
41039              * @param {Calendar} this
41040              * @param {event} Event
41041              */
41042         'evententer': true,
41043         /**
41044              * @event eventleave
41045              * Fires when the mouse leaves an
41046              * @param {Calendar} this
41047              * @param {event}
41048              */
41049         'eventleave': true,
41050         /**
41051              * @event eventclick
41052              * Fires when the mouse click an
41053              * @param {Calendar} this
41054              * @param {event}
41055              */
41056         'eventclick': true,
41057         /**
41058              * @event eventrender
41059              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41060              * @param {Calendar} this
41061              * @param {data} data to be modified
41062              */
41063         'eventrender': true
41064         
41065     });
41066
41067     Roo.grid.Grid.superclass.constructor.call(this);
41068     this.on('render', function() {
41069         this.view.el.addClass('x-grid-cal'); 
41070         
41071         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41072
41073     },this);
41074     
41075     if (!Roo.grid.Calendar.style) {
41076         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41077             
41078             
41079             '.x-grid-cal .x-grid-col' :  {
41080                 height: 'auto !important',
41081                 'vertical-align': 'top'
41082             },
41083             '.x-grid-cal  .fc-event-hori' : {
41084                 height: '14px'
41085             }
41086              
41087             
41088         }, Roo.id());
41089     }
41090
41091     
41092     
41093 };
41094 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41095     /**
41096      * @cfg {Store} eventStore The store that loads events.
41097      */
41098     eventStore : 25,
41099
41100      
41101     activeDate : false,
41102     startDay : 0,
41103     autoWidth : true,
41104     monitorWindowResize : false,
41105
41106     
41107     resizeColumns : function() {
41108         var col = (this.view.el.getWidth() / 7) - 3;
41109         // loop through cols, and setWidth
41110         for(var i =0 ; i < 7 ; i++){
41111             this.cm.setColumnWidth(i, col);
41112         }
41113     },
41114      setDate :function(date) {
41115         
41116         Roo.log('setDate?');
41117         
41118         this.resizeColumns();
41119         var vd = this.activeDate;
41120         this.activeDate = date;
41121 //        if(vd && this.el){
41122 //            var t = date.getTime();
41123 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41124 //                Roo.log('using add remove');
41125 //                
41126 //                this.fireEvent('monthchange', this, date);
41127 //                
41128 //                this.cells.removeClass("fc-state-highlight");
41129 //                this.cells.each(function(c){
41130 //                   if(c.dateValue == t){
41131 //                       c.addClass("fc-state-highlight");
41132 //                       setTimeout(function(){
41133 //                            try{c.dom.firstChild.focus();}catch(e){}
41134 //                       }, 50);
41135 //                       return false;
41136 //                   }
41137 //                   return true;
41138 //                });
41139 //                return;
41140 //            }
41141 //        }
41142         
41143         var days = date.getDaysInMonth();
41144         
41145         var firstOfMonth = date.getFirstDateOfMonth();
41146         var startingPos = firstOfMonth.getDay()-this.startDay;
41147         
41148         if(startingPos < this.startDay){
41149             startingPos += 7;
41150         }
41151         
41152         var pm = date.add(Date.MONTH, -1);
41153         var prevStart = pm.getDaysInMonth()-startingPos;
41154 //        
41155         
41156         
41157         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41158         
41159         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41160         //this.cells.addClassOnOver('fc-state-hover');
41161         
41162         var cells = this.cells.elements;
41163         var textEls = this.textNodes;
41164         
41165         //Roo.each(cells, function(cell){
41166         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41167         //});
41168         
41169         days += startingPos;
41170
41171         // convert everything to numbers so it's fast
41172         var day = 86400000;
41173         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41174         //Roo.log(d);
41175         //Roo.log(pm);
41176         //Roo.log(prevStart);
41177         
41178         var today = new Date().clearTime().getTime();
41179         var sel = date.clearTime().getTime();
41180         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41181         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41182         var ddMatch = this.disabledDatesRE;
41183         var ddText = this.disabledDatesText;
41184         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41185         var ddaysText = this.disabledDaysText;
41186         var format = this.format;
41187         
41188         var setCellClass = function(cal, cell){
41189             
41190             //Roo.log('set Cell Class');
41191             cell.title = "";
41192             var t = d.getTime();
41193             
41194             //Roo.log(d);
41195             
41196             
41197             cell.dateValue = t;
41198             if(t == today){
41199                 cell.className += " fc-today";
41200                 cell.className += " fc-state-highlight";
41201                 cell.title = cal.todayText;
41202             }
41203             if(t == sel){
41204                 // disable highlight in other month..
41205                 cell.className += " fc-state-highlight";
41206                 
41207             }
41208             // disabling
41209             if(t < min) {
41210                 //cell.className = " fc-state-disabled";
41211                 cell.title = cal.minText;
41212                 return;
41213             }
41214             if(t > max) {
41215                 //cell.className = " fc-state-disabled";
41216                 cell.title = cal.maxText;
41217                 return;
41218             }
41219             if(ddays){
41220                 if(ddays.indexOf(d.getDay()) != -1){
41221                     // cell.title = ddaysText;
41222                    // cell.className = " fc-state-disabled";
41223                 }
41224             }
41225             if(ddMatch && format){
41226                 var fvalue = d.dateFormat(format);
41227                 if(ddMatch.test(fvalue)){
41228                     cell.title = ddText.replace("%0", fvalue);
41229                    cell.className = " fc-state-disabled";
41230                 }
41231             }
41232             
41233             if (!cell.initialClassName) {
41234                 cell.initialClassName = cell.dom.className;
41235             }
41236             
41237             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41238         };
41239
41240         var i = 0;
41241         
41242         for(; i < startingPos; i++) {
41243             cells[i].dayName =  (++prevStart);
41244             Roo.log(textEls[i]);
41245             d.setDate(d.getDate()+1);
41246             
41247             //cells[i].className = "fc-past fc-other-month";
41248             setCellClass(this, cells[i]);
41249         }
41250         
41251         var intDay = 0;
41252         
41253         for(; i < days; i++){
41254             intDay = i - startingPos + 1;
41255             cells[i].dayName =  (intDay);
41256             d.setDate(d.getDate()+1);
41257             
41258             cells[i].className = ''; // "x-date-active";
41259             setCellClass(this, cells[i]);
41260         }
41261         var extraDays = 0;
41262         
41263         for(; i < 42; i++) {
41264             //textEls[i].innerHTML = (++extraDays);
41265             
41266             d.setDate(d.getDate()+1);
41267             cells[i].dayName = (++extraDays);
41268             cells[i].className = "fc-future fc-other-month";
41269             setCellClass(this, cells[i]);
41270         }
41271         
41272         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41273         
41274         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41275         
41276         // this will cause all the cells to mis
41277         var rows= [];
41278         var i =0;
41279         for (var r = 0;r < 6;r++) {
41280             for (var c =0;c < 7;c++) {
41281                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41282             }    
41283         }
41284         
41285         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41286         for(i=0;i<cells.length;i++) {
41287             
41288             this.cells.elements[i].dayName = cells[i].dayName ;
41289             this.cells.elements[i].className = cells[i].className;
41290             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41291             this.cells.elements[i].title = cells[i].title ;
41292             this.cells.elements[i].dateValue = cells[i].dateValue ;
41293         }
41294         
41295         
41296         
41297         
41298         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41299         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41300         
41301         ////if(totalRows != 6){
41302             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41303            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41304        // }
41305         
41306         this.fireEvent('monthchange', this, date);
41307         
41308         
41309     },
41310  /**
41311      * Returns the grid's SelectionModel.
41312      * @return {SelectionModel}
41313      */
41314     getSelectionModel : function(){
41315         if(!this.selModel){
41316             this.selModel = new Roo.grid.CellSelectionModel();
41317         }
41318         return this.selModel;
41319     },
41320
41321     load: function() {
41322         this.eventStore.load()
41323         
41324         
41325         
41326     },
41327     
41328     findCell : function(dt) {
41329         dt = dt.clearTime().getTime();
41330         var ret = false;
41331         this.cells.each(function(c){
41332             //Roo.log("check " +c.dateValue + '?=' + dt);
41333             if(c.dateValue == dt){
41334                 ret = c;
41335                 return false;
41336             }
41337             return true;
41338         });
41339         
41340         return ret;
41341     },
41342     
41343     findCells : function(rec) {
41344         var s = rec.data.start_dt.clone().clearTime().getTime();
41345        // Roo.log(s);
41346         var e= rec.data.end_dt.clone().clearTime().getTime();
41347        // Roo.log(e);
41348         var ret = [];
41349         this.cells.each(function(c){
41350              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41351             
41352             if(c.dateValue > e){
41353                 return ;
41354             }
41355             if(c.dateValue < s){
41356                 return ;
41357             }
41358             ret.push(c);
41359         });
41360         
41361         return ret;    
41362     },
41363     
41364     findBestRow: function(cells)
41365     {
41366         var ret = 0;
41367         
41368         for (var i =0 ; i < cells.length;i++) {
41369             ret  = Math.max(cells[i].rows || 0,ret);
41370         }
41371         return ret;
41372         
41373     },
41374     
41375     
41376     addItem : function(rec)
41377     {
41378         // look for vertical location slot in
41379         var cells = this.findCells(rec);
41380         
41381         rec.row = this.findBestRow(cells);
41382         
41383         // work out the location.
41384         
41385         var crow = false;
41386         var rows = [];
41387         for(var i =0; i < cells.length; i++) {
41388             if (!crow) {
41389                 crow = {
41390                     start : cells[i],
41391                     end :  cells[i]
41392                 };
41393                 continue;
41394             }
41395             if (crow.start.getY() == cells[i].getY()) {
41396                 // on same row.
41397                 crow.end = cells[i];
41398                 continue;
41399             }
41400             // different row.
41401             rows.push(crow);
41402             crow = {
41403                 start: cells[i],
41404                 end : cells[i]
41405             };
41406             
41407         }
41408         
41409         rows.push(crow);
41410         rec.els = [];
41411         rec.rows = rows;
41412         rec.cells = cells;
41413         for (var i = 0; i < cells.length;i++) {
41414             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41415             
41416         }
41417         
41418         
41419     },
41420     
41421     clearEvents: function() {
41422         
41423         if (!this.eventStore.getCount()) {
41424             return;
41425         }
41426         // reset number of rows in cells.
41427         Roo.each(this.cells.elements, function(c){
41428             c.rows = 0;
41429         });
41430         
41431         this.eventStore.each(function(e) {
41432             this.clearEvent(e);
41433         },this);
41434         
41435     },
41436     
41437     clearEvent : function(ev)
41438     {
41439         if (ev.els) {
41440             Roo.each(ev.els, function(el) {
41441                 el.un('mouseenter' ,this.onEventEnter, this);
41442                 el.un('mouseleave' ,this.onEventLeave, this);
41443                 el.remove();
41444             },this);
41445             ev.els = [];
41446         }
41447     },
41448     
41449     
41450     renderEvent : function(ev,ctr) {
41451         if (!ctr) {
41452              ctr = this.view.el.select('.fc-event-container',true).first();
41453         }
41454         
41455          
41456         this.clearEvent(ev);
41457             //code
41458        
41459         
41460         
41461         ev.els = [];
41462         var cells = ev.cells;
41463         var rows = ev.rows;
41464         this.fireEvent('eventrender', this, ev);
41465         
41466         for(var i =0; i < rows.length; i++) {
41467             
41468             cls = '';
41469             if (i == 0) {
41470                 cls += ' fc-event-start';
41471             }
41472             if ((i+1) == rows.length) {
41473                 cls += ' fc-event-end';
41474             }
41475             
41476             //Roo.log(ev.data);
41477             // how many rows should it span..
41478             var cg = this.eventTmpl.append(ctr,Roo.apply({
41479                 fccls : cls
41480                 
41481             }, ev.data) , true);
41482             
41483             
41484             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41485             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41486             cg.on('click', this.onEventClick, this, ev);
41487             
41488             ev.els.push(cg);
41489             
41490             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41491             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41492             //Roo.log(cg);
41493              
41494             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41495             cg.setWidth(ebox.right - sbox.x -2);
41496         }
41497     },
41498     
41499     renderEvents: function()
41500     {   
41501         // first make sure there is enough space..
41502         
41503         if (!this.eventTmpl) {
41504             this.eventTmpl = new Roo.Template(
41505                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41506                     '<div class="fc-event-inner">' +
41507                         '<span class="fc-event-time">{time}</span>' +
41508                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41509                     '</div>' +
41510                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41511                 '</div>'
41512             );
41513                 
41514         }
41515                
41516         
41517         
41518         this.cells.each(function(c) {
41519             //Roo.log(c.select('.fc-day-content div',true).first());
41520             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41521         });
41522         
41523         var ctr = this.view.el.select('.fc-event-container',true).first();
41524         
41525         var cls;
41526         this.eventStore.each(function(ev){
41527             
41528             this.renderEvent(ev);
41529              
41530              
41531         }, this);
41532         this.view.layout();
41533         
41534     },
41535     
41536     onEventEnter: function (e, el,event,d) {
41537         this.fireEvent('evententer', this, el, event);
41538     },
41539     
41540     onEventLeave: function (e, el,event,d) {
41541         this.fireEvent('eventleave', this, el, event);
41542     },
41543     
41544     onEventClick: function (e, el,event,d) {
41545         this.fireEvent('eventclick', this, el, event);
41546     },
41547     
41548     onMonthChange: function () {
41549         this.store.load();
41550     },
41551     
41552     onLoad: function () {
41553         
41554         //Roo.log('calendar onload');
41555 //         
41556         if(this.eventStore.getCount() > 0){
41557             
41558            
41559             
41560             this.eventStore.each(function(d){
41561                 
41562                 
41563                 // FIXME..
41564                 var add =   d.data;
41565                 if (typeof(add.end_dt) == 'undefined')  {
41566                     Roo.log("Missing End time in calendar data: ");
41567                     Roo.log(d);
41568                     return;
41569                 }
41570                 if (typeof(add.start_dt) == 'undefined')  {
41571                     Roo.log("Missing Start time in calendar data: ");
41572                     Roo.log(d);
41573                     return;
41574                 }
41575                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41576                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41577                 add.id = add.id || d.id;
41578                 add.title = add.title || '??';
41579                 
41580                 this.addItem(d);
41581                 
41582              
41583             },this);
41584         }
41585         
41586         this.renderEvents();
41587     }
41588     
41589
41590 });
41591 /*
41592  grid : {
41593                 xtype: 'Grid',
41594                 xns: Roo.grid,
41595                 listeners : {
41596                     render : function ()
41597                     {
41598                         _this.grid = this;
41599                         
41600                         if (!this.view.el.hasClass('course-timesheet')) {
41601                             this.view.el.addClass('course-timesheet');
41602                         }
41603                         if (this.tsStyle) {
41604                             this.ds.load({});
41605                             return; 
41606                         }
41607                         Roo.log('width');
41608                         Roo.log(_this.grid.view.el.getWidth());
41609                         
41610                         
41611                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41612                             '.course-timesheet .x-grid-row' : {
41613                                 height: '80px'
41614                             },
41615                             '.x-grid-row td' : {
41616                                 'vertical-align' : 0
41617                             },
41618                             '.course-edit-link' : {
41619                                 'color' : 'blue',
41620                                 'text-overflow' : 'ellipsis',
41621                                 'overflow' : 'hidden',
41622                                 'white-space' : 'nowrap',
41623                                 'cursor' : 'pointer'
41624                             },
41625                             '.sub-link' : {
41626                                 'color' : 'green'
41627                             },
41628                             '.de-act-sup-link' : {
41629                                 'color' : 'purple',
41630                                 'text-decoration' : 'line-through'
41631                             },
41632                             '.de-act-link' : {
41633                                 'color' : 'red',
41634                                 'text-decoration' : 'line-through'
41635                             },
41636                             '.course-timesheet .course-highlight' : {
41637                                 'border-top-style': 'dashed !important',
41638                                 'border-bottom-bottom': 'dashed !important'
41639                             },
41640                             '.course-timesheet .course-item' : {
41641                                 'font-family'   : 'tahoma, arial, helvetica',
41642                                 'font-size'     : '11px',
41643                                 'overflow'      : 'hidden',
41644                                 'padding-left'  : '10px',
41645                                 'padding-right' : '10px',
41646                                 'padding-top' : '10px' 
41647                             }
41648                             
41649                         }, Roo.id());
41650                                 this.ds.load({});
41651                     }
41652                 },
41653                 autoWidth : true,
41654                 monitorWindowResize : false,
41655                 cellrenderer : function(v,x,r)
41656                 {
41657                     return v;
41658                 },
41659                 sm : {
41660                     xtype: 'CellSelectionModel',
41661                     xns: Roo.grid
41662                 },
41663                 dataSource : {
41664                     xtype: 'Store',
41665                     xns: Roo.data,
41666                     listeners : {
41667                         beforeload : function (_self, options)
41668                         {
41669                             options.params = options.params || {};
41670                             options.params._month = _this.monthField.getValue();
41671                             options.params.limit = 9999;
41672                             options.params['sort'] = 'when_dt';    
41673                             options.params['dir'] = 'ASC';    
41674                             this.proxy.loadResponse = this.loadResponse;
41675                             Roo.log("load?");
41676                             //this.addColumns();
41677                         },
41678                         load : function (_self, records, options)
41679                         {
41680                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41681                                 // if you click on the translation.. you can edit it...
41682                                 var el = Roo.get(this);
41683                                 var id = el.dom.getAttribute('data-id');
41684                                 var d = el.dom.getAttribute('data-date');
41685                                 var t = el.dom.getAttribute('data-time');
41686                                 //var id = this.child('span').dom.textContent;
41687                                 
41688                                 //Roo.log(this);
41689                                 Pman.Dialog.CourseCalendar.show({
41690                                     id : id,
41691                                     when_d : d,
41692                                     when_t : t,
41693                                     productitem_active : id ? 1 : 0
41694                                 }, function() {
41695                                     _this.grid.ds.load({});
41696                                 });
41697                            
41698                            });
41699                            
41700                            _this.panel.fireEvent('resize', [ '', '' ]);
41701                         }
41702                     },
41703                     loadResponse : function(o, success, response){
41704                             // this is overridden on before load..
41705                             
41706                             Roo.log("our code?");       
41707                             //Roo.log(success);
41708                             //Roo.log(response)
41709                             delete this.activeRequest;
41710                             if(!success){
41711                                 this.fireEvent("loadexception", this, o, response);
41712                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41713                                 return;
41714                             }
41715                             var result;
41716                             try {
41717                                 result = o.reader.read(response);
41718                             }catch(e){
41719                                 Roo.log("load exception?");
41720                                 this.fireEvent("loadexception", this, o, response, e);
41721                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41722                                 return;
41723                             }
41724                             Roo.log("ready...");        
41725                             // loop through result.records;
41726                             // and set this.tdate[date] = [] << array of records..
41727                             _this.tdata  = {};
41728                             Roo.each(result.records, function(r){
41729                                 //Roo.log(r.data);
41730                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41731                                     _this.tdata[r.data.when_dt.format('j')] = [];
41732                                 }
41733                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41734                             });
41735                             
41736                             //Roo.log(_this.tdata);
41737                             
41738                             result.records = [];
41739                             result.totalRecords = 6;
41740                     
41741                             // let's generate some duumy records for the rows.
41742                             //var st = _this.dateField.getValue();
41743                             
41744                             // work out monday..
41745                             //st = st.add(Date.DAY, -1 * st.format('w'));
41746                             
41747                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41748                             
41749                             var firstOfMonth = date.getFirstDayOfMonth();
41750                             var days = date.getDaysInMonth();
41751                             var d = 1;
41752                             var firstAdded = false;
41753                             for (var i = 0; i < result.totalRecords ; i++) {
41754                                 //var d= st.add(Date.DAY, i);
41755                                 var row = {};
41756                                 var added = 0;
41757                                 for(var w = 0 ; w < 7 ; w++){
41758                                     if(!firstAdded && firstOfMonth != w){
41759                                         continue;
41760                                     }
41761                                     if(d > days){
41762                                         continue;
41763                                     }
41764                                     firstAdded = true;
41765                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41766                                     row['weekday'+w] = String.format(
41767                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41768                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41769                                                     d,
41770                                                     date.format('Y-m-')+dd
41771                                                 );
41772                                     added++;
41773                                     if(typeof(_this.tdata[d]) != 'undefined'){
41774                                         Roo.each(_this.tdata[d], function(r){
41775                                             var is_sub = '';
41776                                             var deactive = '';
41777                                             var id = r.id;
41778                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41779                                             if(r.parent_id*1>0){
41780                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41781                                                 id = r.parent_id;
41782                                             }
41783                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41784                                                 deactive = 'de-act-link';
41785                                             }
41786                                             
41787                                             row['weekday'+w] += String.format(
41788                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41789                                                     id, //0
41790                                                     r.product_id_name, //1
41791                                                     r.when_dt.format('h:ia'), //2
41792                                                     is_sub, //3
41793                                                     deactive, //4
41794                                                     desc // 5
41795                                             );
41796                                         });
41797                                     }
41798                                     d++;
41799                                 }
41800                                 
41801                                 // only do this if something added..
41802                                 if(added > 0){ 
41803                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41804                                 }
41805                                 
41806                                 
41807                                 // push it twice. (second one with an hour..
41808                                 
41809                             }
41810                             //Roo.log(result);
41811                             this.fireEvent("load", this, o, o.request.arg);
41812                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41813                         },
41814                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41815                     proxy : {
41816                         xtype: 'HttpProxy',
41817                         xns: Roo.data,
41818                         method : 'GET',
41819                         url : baseURL + '/Roo/Shop_course.php'
41820                     },
41821                     reader : {
41822                         xtype: 'JsonReader',
41823                         xns: Roo.data,
41824                         id : 'id',
41825                         fields : [
41826                             {
41827                                 'name': 'id',
41828                                 'type': 'int'
41829                             },
41830                             {
41831                                 'name': 'when_dt',
41832                                 'type': 'string'
41833                             },
41834                             {
41835                                 'name': 'end_dt',
41836                                 'type': 'string'
41837                             },
41838                             {
41839                                 'name': 'parent_id',
41840                                 'type': 'int'
41841                             },
41842                             {
41843                                 'name': 'product_id',
41844                                 'type': 'int'
41845                             },
41846                             {
41847                                 'name': 'productitem_id',
41848                                 'type': 'int'
41849                             },
41850                             {
41851                                 'name': 'guid',
41852                                 'type': 'int'
41853                             }
41854                         ]
41855                     }
41856                 },
41857                 toolbar : {
41858                     xtype: 'Toolbar',
41859                     xns: Roo,
41860                     items : [
41861                         {
41862                             xtype: 'Button',
41863                             xns: Roo.Toolbar,
41864                             listeners : {
41865                                 click : function (_self, e)
41866                                 {
41867                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41868                                     sd.setMonth(sd.getMonth()-1);
41869                                     _this.monthField.setValue(sd.format('Y-m-d'));
41870                                     _this.grid.ds.load({});
41871                                 }
41872                             },
41873                             text : "Back"
41874                         },
41875                         {
41876                             xtype: 'Separator',
41877                             xns: Roo.Toolbar
41878                         },
41879                         {
41880                             xtype: 'MonthField',
41881                             xns: Roo.form,
41882                             listeners : {
41883                                 render : function (_self)
41884                                 {
41885                                     _this.monthField = _self;
41886                                    // _this.monthField.set  today
41887                                 },
41888                                 select : function (combo, date)
41889                                 {
41890                                     _this.grid.ds.load({});
41891                                 }
41892                             },
41893                             value : (function() { return new Date(); })()
41894                         },
41895                         {
41896                             xtype: 'Separator',
41897                             xns: Roo.Toolbar
41898                         },
41899                         {
41900                             xtype: 'TextItem',
41901                             xns: Roo.Toolbar,
41902                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41903                         },
41904                         {
41905                             xtype: 'Fill',
41906                             xns: Roo.Toolbar
41907                         },
41908                         {
41909                             xtype: 'Button',
41910                             xns: Roo.Toolbar,
41911                             listeners : {
41912                                 click : function (_self, e)
41913                                 {
41914                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41915                                     sd.setMonth(sd.getMonth()+1);
41916                                     _this.monthField.setValue(sd.format('Y-m-d'));
41917                                     _this.grid.ds.load({});
41918                                 }
41919                             },
41920                             text : "Next"
41921                         }
41922                     ]
41923                 },
41924                  
41925             }
41926         };
41927         
41928         *//*
41929  * Based on:
41930  * Ext JS Library 1.1.1
41931  * Copyright(c) 2006-2007, Ext JS, LLC.
41932  *
41933  * Originally Released Under LGPL - original licence link has changed is not relivant.
41934  *
41935  * Fork - LGPL
41936  * <script type="text/javascript">
41937  */
41938  
41939 /**
41940  * @class Roo.LoadMask
41941  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41942  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41943  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41944  * element's UpdateManager load indicator and will be destroyed after the initial load.
41945  * @constructor
41946  * Create a new LoadMask
41947  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41948  * @param {Object} config The config object
41949  */
41950 Roo.LoadMask = function(el, config){
41951     this.el = Roo.get(el);
41952     Roo.apply(this, config);
41953     if(this.store){
41954         this.store.on('beforeload', this.onBeforeLoad, this);
41955         this.store.on('load', this.onLoad, this);
41956         this.store.on('loadexception', this.onLoadException, this);
41957         this.removeMask = false;
41958     }else{
41959         var um = this.el.getUpdateManager();
41960         um.showLoadIndicator = false; // disable the default indicator
41961         um.on('beforeupdate', this.onBeforeLoad, this);
41962         um.on('update', this.onLoad, this);
41963         um.on('failure', this.onLoad, this);
41964         this.removeMask = true;
41965     }
41966 };
41967
41968 Roo.LoadMask.prototype = {
41969     /**
41970      * @cfg {Boolean} removeMask
41971      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41972      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41973      */
41974     /**
41975      * @cfg {String} msg
41976      * The text to display in a centered loading message box (defaults to 'Loading...')
41977      */
41978     msg : 'Loading...',
41979     /**
41980      * @cfg {String} msgCls
41981      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41982      */
41983     msgCls : 'x-mask-loading',
41984
41985     /**
41986      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41987      * @type Boolean
41988      */
41989     disabled: false,
41990
41991     /**
41992      * Disables the mask to prevent it from being displayed
41993      */
41994     disable : function(){
41995        this.disabled = true;
41996     },
41997
41998     /**
41999      * Enables the mask so that it can be displayed
42000      */
42001     enable : function(){
42002         this.disabled = false;
42003     },
42004     
42005     onLoadException : function()
42006     {
42007         Roo.log(arguments);
42008         
42009         if (typeof(arguments[3]) != 'undefined') {
42010             Roo.MessageBox.alert("Error loading",arguments[3]);
42011         } 
42012         /*
42013         try {
42014             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42015                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42016             }   
42017         } catch(e) {
42018             
42019         }
42020         */
42021     
42022         
42023         
42024         this.el.unmask(this.removeMask);
42025     },
42026     // private
42027     onLoad : function()
42028     {
42029         this.el.unmask(this.removeMask);
42030     },
42031
42032     // private
42033     onBeforeLoad : function(){
42034         if(!this.disabled){
42035             this.el.mask(this.msg, this.msgCls);
42036         }
42037     },
42038
42039     // private
42040     destroy : function(){
42041         if(this.store){
42042             this.store.un('beforeload', this.onBeforeLoad, this);
42043             this.store.un('load', this.onLoad, this);
42044             this.store.un('loadexception', this.onLoadException, this);
42045         }else{
42046             var um = this.el.getUpdateManager();
42047             um.un('beforeupdate', this.onBeforeLoad, this);
42048             um.un('update', this.onLoad, this);
42049             um.un('failure', this.onLoad, this);
42050         }
42051     }
42052 };/*
42053  * Based on:
42054  * Ext JS Library 1.1.1
42055  * Copyright(c) 2006-2007, Ext JS, LLC.
42056  *
42057  * Originally Released Under LGPL - original licence link has changed is not relivant.
42058  *
42059  * Fork - LGPL
42060  * <script type="text/javascript">
42061  */
42062
42063
42064 /**
42065  * @class Roo.XTemplate
42066  * @extends Roo.Template
42067  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42068 <pre><code>
42069 var t = new Roo.XTemplate(
42070         '&lt;select name="{name}"&gt;',
42071                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42072         '&lt;/select&gt;'
42073 );
42074  
42075 // then append, applying the master template values
42076  </code></pre>
42077  *
42078  * Supported features:
42079  *
42080  *  Tags:
42081
42082 <pre><code>
42083       {a_variable} - output encoded.
42084       {a_variable.format:("Y-m-d")} - call a method on the variable
42085       {a_variable:raw} - unencoded output
42086       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42087       {a_variable:this.method_on_template(...)} - call a method on the template object.
42088  
42089 </code></pre>
42090  *  The tpl tag:
42091 <pre><code>
42092         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42093         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42094         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42095         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42096   
42097         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42098         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42099 </code></pre>
42100  *      
42101  */
42102 Roo.XTemplate = function()
42103 {
42104     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42105     if (this.html) {
42106         this.compile();
42107     }
42108 };
42109
42110
42111 Roo.extend(Roo.XTemplate, Roo.Template, {
42112
42113     /**
42114      * The various sub templates
42115      */
42116     tpls : false,
42117     /**
42118      *
42119      * basic tag replacing syntax
42120      * WORD:WORD()
42121      *
42122      * // you can fake an object call by doing this
42123      *  x.t:(test,tesT) 
42124      * 
42125      */
42126     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42127
42128     /**
42129      * compile the template
42130      *
42131      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42132      *
42133      */
42134     compile: function()
42135     {
42136         var s = this.html;
42137      
42138         s = ['<tpl>', s, '</tpl>'].join('');
42139     
42140         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42141             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42142             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42143             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42144             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42145             m,
42146             id     = 0,
42147             tpls   = [];
42148     
42149         while(true == !!(m = s.match(re))){
42150             var forMatch   = m[0].match(nameRe),
42151                 ifMatch   = m[0].match(ifRe),
42152                 execMatch   = m[0].match(execRe),
42153                 namedMatch   = m[0].match(namedRe),
42154                 
42155                 exp  = null, 
42156                 fn   = null,
42157                 exec = null,
42158                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42159                 
42160             if (ifMatch) {
42161                 // if - puts fn into test..
42162                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42163                 if(exp){
42164                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42165                 }
42166             }
42167             
42168             if (execMatch) {
42169                 // exec - calls a function... returns empty if true is  returned.
42170                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42171                 if(exp){
42172                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42173                 }
42174             }
42175             
42176             
42177             if (name) {
42178                 // for = 
42179                 switch(name){
42180                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42181                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42182                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42183                 }
42184             }
42185             var uid = namedMatch ? namedMatch[1] : id;
42186             
42187             
42188             tpls.push({
42189                 id:     namedMatch ? namedMatch[1] : id,
42190                 target: name,
42191                 exec:   exec,
42192                 test:   fn,
42193                 body:   m[1] || ''
42194             });
42195             if (namedMatch) {
42196                 s = s.replace(m[0], '');
42197             } else { 
42198                 s = s.replace(m[0], '{xtpl'+ id + '}');
42199             }
42200             ++id;
42201         }
42202         this.tpls = [];
42203         for(var i = tpls.length-1; i >= 0; --i){
42204             this.compileTpl(tpls[i]);
42205             this.tpls[tpls[i].id] = tpls[i];
42206         }
42207         this.master = tpls[tpls.length-1];
42208         return this;
42209     },
42210     /**
42211      * same as applyTemplate, except it's done to one of the subTemplates
42212      * when using named templates, you can do:
42213      *
42214      * var str = pl.applySubTemplate('your-name', values);
42215      *
42216      * 
42217      * @param {Number} id of the template
42218      * @param {Object} values to apply to template
42219      * @param {Object} parent (normaly the instance of this object)
42220      */
42221     applySubTemplate : function(id, values, parent)
42222     {
42223         
42224         
42225         var t = this.tpls[id];
42226         
42227         
42228         try { 
42229             if(t.test && !t.test.call(this, values, parent)){
42230                 return '';
42231             }
42232         } catch(e) {
42233             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42234             Roo.log(e.toString());
42235             Roo.log(t.test);
42236             return ''
42237         }
42238         try { 
42239             
42240             if(t.exec && t.exec.call(this, values, parent)){
42241                 return '';
42242             }
42243         } catch(e) {
42244             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42245             Roo.log(e.toString());
42246             Roo.log(t.exec);
42247             return ''
42248         }
42249         try {
42250             var vs = t.target ? t.target.call(this, values, parent) : values;
42251             parent = t.target ? values : parent;
42252             if(t.target && vs instanceof Array){
42253                 var buf = [];
42254                 for(var i = 0, len = vs.length; i < len; i++){
42255                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42256                 }
42257                 return buf.join('');
42258             }
42259             return t.compiled.call(this, vs, parent);
42260         } catch (e) {
42261             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42262             Roo.log(e.toString());
42263             Roo.log(t.compiled);
42264             return '';
42265         }
42266     },
42267
42268     compileTpl : function(tpl)
42269     {
42270         var fm = Roo.util.Format;
42271         var useF = this.disableFormats !== true;
42272         var sep = Roo.isGecko ? "+" : ",";
42273         var undef = function(str) {
42274             Roo.log("Property not found :"  + str);
42275             return '';
42276         };
42277         
42278         var fn = function(m, name, format, args)
42279         {
42280             //Roo.log(arguments);
42281             args = args ? args.replace(/\\'/g,"'") : args;
42282             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42283             if (typeof(format) == 'undefined') {
42284                 format= 'htmlEncode';
42285             }
42286             if (format == 'raw' ) {
42287                 format = false;
42288             }
42289             
42290             if(name.substr(0, 4) == 'xtpl'){
42291                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42292             }
42293             
42294             // build an array of options to determine if value is undefined..
42295             
42296             // basically get 'xxxx.yyyy' then do
42297             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42298             //    (function () { Roo.log("Property not found"); return ''; })() :
42299             //    ......
42300             
42301             var udef_ar = [];
42302             var lookfor = '';
42303             Roo.each(name.split('.'), function(st) {
42304                 lookfor += (lookfor.length ? '.': '') + st;
42305                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42306             });
42307             
42308             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42309             
42310             
42311             if(format && useF){
42312                 
42313                 args = args ? ',' + args : "";
42314                  
42315                 if(format.substr(0, 5) != "this."){
42316                     format = "fm." + format + '(';
42317                 }else{
42318                     format = 'this.call("'+ format.substr(5) + '", ';
42319                     args = ", values";
42320                 }
42321                 
42322                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42323             }
42324              
42325             if (args.length) {
42326                 // called with xxyx.yuu:(test,test)
42327                 // change to ()
42328                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42329             }
42330             // raw.. - :raw modifier..
42331             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42332             
42333         };
42334         var body;
42335         // branched to use + in gecko and [].join() in others
42336         if(Roo.isGecko){
42337             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42338                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42339                     "';};};";
42340         }else{
42341             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42342             body.push(tpl.body.replace(/(\r\n|\n)/g,
42343                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42344             body.push("'].join('');};};");
42345             body = body.join('');
42346         }
42347         
42348         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42349        
42350         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42351         eval(body);
42352         
42353         return this;
42354     },
42355
42356     applyTemplate : function(values){
42357         return this.master.compiled.call(this, values, {});
42358         //var s = this.subs;
42359     },
42360
42361     apply : function(){
42362         return this.applyTemplate.apply(this, arguments);
42363     }
42364
42365  });
42366
42367 Roo.XTemplate.from = function(el){
42368     el = Roo.getDom(el);
42369     return new Roo.XTemplate(el.value || el.innerHTML);
42370 };