roojs-core.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) val = 0;
4462         return val;
4463     },
4464     
4465     /**
4466      * Integer sorting
4467      * @param {Mixed} s The value being converted
4468      * @return {Number} The comparison value
4469      */
4470     asInt : function(s) {
4471         var val = parseInt(String(s).replace(/,/g, ""));
4472         if(isNaN(val)) val = 0;
4473         return val;
4474     }
4475 };/*
4476  * Based on:
4477  * Ext JS Library 1.1.1
4478  * Copyright(c) 2006-2007, Ext JS, LLC.
4479  *
4480  * Originally Released Under LGPL - original licence link has changed is not relivant.
4481  *
4482  * Fork - LGPL
4483  * <script type="text/javascript">
4484  */
4485
4486 /**
4487 * @class Roo.data.Record
4488  * Instances of this class encapsulate both record <em>definition</em> information, and record
4489  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4490  * to access Records cached in an {@link Roo.data.Store} object.<br>
4491  * <p>
4492  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4493  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4494  * objects.<br>
4495  * <p>
4496  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4497  * @constructor
4498  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4499  * {@link #create}. The parameters are the same.
4500  * @param {Array} data An associative Array of data values keyed by the field name.
4501  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4502  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4503  * not specified an integer id is generated.
4504  */
4505 Roo.data.Record = function(data, id){
4506     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4507     this.data = data;
4508 };
4509
4510 /**
4511  * Generate a constructor for a specific record layout.
4512  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4513  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4514  * Each field definition object may contain the following properties: <ul>
4515  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4516  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4517  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4518  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4519  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4520  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4521  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4522  * this may be omitted.</p></li>
4523  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4524  * <ul><li>auto (Default, implies no conversion)</li>
4525  * <li>string</li>
4526  * <li>int</li>
4527  * <li>float</li>
4528  * <li>boolean</li>
4529  * <li>date</li></ul></p></li>
4530  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4531  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4532  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4533  * by the Reader into an object that will be stored in the Record. It is passed the
4534  * following parameters:<ul>
4535  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4536  * </ul></p></li>
4537  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4538  * </ul>
4539  * <br>usage:<br><pre><code>
4540 var TopicRecord = Roo.data.Record.create(
4541     {name: 'title', mapping: 'topic_title'},
4542     {name: 'author', mapping: 'username'},
4543     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4544     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4545     {name: 'lastPoster', mapping: 'user2'},
4546     {name: 'excerpt', mapping: 'post_text'}
4547 );
4548
4549 var myNewRecord = new TopicRecord({
4550     title: 'Do my job please',
4551     author: 'noobie',
4552     totalPosts: 1,
4553     lastPost: new Date(),
4554     lastPoster: 'Animal',
4555     excerpt: 'No way dude!'
4556 });
4557 myStore.add(myNewRecord);
4558 </code></pre>
4559  * @method create
4560  * @static
4561  */
4562 Roo.data.Record.create = function(o){
4563     var f = function(){
4564         f.superclass.constructor.apply(this, arguments);
4565     };
4566     Roo.extend(f, Roo.data.Record);
4567     var p = f.prototype;
4568     p.fields = new Roo.util.MixedCollection(false, function(field){
4569         return field.name;
4570     });
4571     for(var i = 0, len = o.length; i < len; i++){
4572         p.fields.add(new Roo.data.Field(o[i]));
4573     }
4574     f.getField = function(name){
4575         return p.fields.get(name);  
4576     };
4577     return f;
4578 };
4579
4580 Roo.data.Record.AUTO_ID = 1000;
4581 Roo.data.Record.EDIT = 'edit';
4582 Roo.data.Record.REJECT = 'reject';
4583 Roo.data.Record.COMMIT = 'commit';
4584
4585 Roo.data.Record.prototype = {
4586     /**
4587      * Readonly flag - true if this record has been modified.
4588      * @type Boolean
4589      */
4590     dirty : false,
4591     editing : false,
4592     error: null,
4593     modified: null,
4594
4595     // private
4596     join : function(store){
4597         this.store = store;
4598     },
4599
4600     /**
4601      * Set the named field to the specified value.
4602      * @param {String} name The name of the field to set.
4603      * @param {Object} value The value to set the field to.
4604      */
4605     set : function(name, value){
4606         if(this.data[name] == value){
4607             return;
4608         }
4609         this.dirty = true;
4610         if(!this.modified){
4611             this.modified = {};
4612         }
4613         if(typeof this.modified[name] == 'undefined'){
4614             this.modified[name] = this.data[name];
4615         }
4616         this.data[name] = value;
4617         if(!this.editing && this.store){
4618             this.store.afterEdit(this);
4619         }       
4620     },
4621
4622     /**
4623      * Get the value of the named field.
4624      * @param {String} name The name of the field to get the value of.
4625      * @return {Object} The value of the field.
4626      */
4627     get : function(name){
4628         return this.data[name]; 
4629     },
4630
4631     // private
4632     beginEdit : function(){
4633         this.editing = true;
4634         this.modified = {}; 
4635     },
4636
4637     // private
4638     cancelEdit : function(){
4639         this.editing = false;
4640         delete this.modified;
4641     },
4642
4643     // private
4644     endEdit : function(){
4645         this.editing = false;
4646         if(this.dirty && this.store){
4647             this.store.afterEdit(this);
4648         }
4649     },
4650
4651     /**
4652      * Usually called by the {@link Roo.data.Store} which owns the Record.
4653      * Rejects all changes made to the Record since either creation, or the last commit operation.
4654      * Modified fields are reverted to their original values.
4655      * <p>
4656      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4657      * of reject operations.
4658      */
4659     reject : function(){
4660         var m = this.modified;
4661         for(var n in m){
4662             if(typeof m[n] != "function"){
4663                 this.data[n] = m[n];
4664             }
4665         }
4666         this.dirty = false;
4667         delete this.modified;
4668         this.editing = false;
4669         if(this.store){
4670             this.store.afterReject(this);
4671         }
4672     },
4673
4674     /**
4675      * Usually called by the {@link Roo.data.Store} which owns the Record.
4676      * Commits all changes made to the Record since either creation, or the last commit operation.
4677      * <p>
4678      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4679      * of commit operations.
4680      */
4681     commit : function(){
4682         this.dirty = false;
4683         delete this.modified;
4684         this.editing = false;
4685         if(this.store){
4686             this.store.afterCommit(this);
4687         }
4688     },
4689
4690     // private
4691     hasError : function(){
4692         return this.error != null;
4693     },
4694
4695     // private
4696     clearError : function(){
4697         this.error = null;
4698     },
4699
4700     /**
4701      * Creates a copy of this record.
4702      * @param {String} id (optional) A new record id if you don't want to use this record's id
4703      * @return {Record}
4704      */
4705     copy : function(newId) {
4706         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4707     }
4708 };/*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718
4719
4720
4721 /**
4722  * @class Roo.data.Store
4723  * @extends Roo.util.Observable
4724  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4725  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4726  * <p>
4727  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4728  * has no knowledge of the format of the data returned by the Proxy.<br>
4729  * <p>
4730  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4731  * instances from the data object. These records are cached and made available through accessor functions.
4732  * @constructor
4733  * Creates a new Store.
4734  * @param {Object} config A config object containing the objects needed for the Store to access data,
4735  * and read the data into Records.
4736  */
4737 Roo.data.Store = function(config){
4738     this.data = new Roo.util.MixedCollection(false);
4739     this.data.getKey = function(o){
4740         return o.id;
4741     };
4742     this.baseParams = {};
4743     // private
4744     this.paramNames = {
4745         "start" : "start",
4746         "limit" : "limit",
4747         "sort" : "sort",
4748         "dir" : "dir",
4749         "multisort" : "_multisort"
4750     };
4751
4752     if(config && config.data){
4753         this.inlineData = config.data;
4754         delete config.data;
4755     }
4756
4757     Roo.apply(this, config);
4758     
4759     if(this.reader){ // reader passed
4760         this.reader = Roo.factory(this.reader, Roo.data);
4761         this.reader.xmodule = this.xmodule || false;
4762         if(!this.recordType){
4763             this.recordType = this.reader.recordType;
4764         }
4765         if(this.reader.onMetaChange){
4766             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4767         }
4768     }
4769
4770     if(this.recordType){
4771         this.fields = this.recordType.prototype.fields;
4772     }
4773     this.modified = [];
4774
4775     this.addEvents({
4776         /**
4777          * @event datachanged
4778          * Fires when the data cache has changed, and a widget which is using this Store
4779          * as a Record cache should refresh its view.
4780          * @param {Store} this
4781          */
4782         datachanged : true,
4783         /**
4784          * @event metachange
4785          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4786          * @param {Store} this
4787          * @param {Object} meta The JSON metadata
4788          */
4789         metachange : true,
4790         /**
4791          * @event add
4792          * Fires when Records have been added to the Store
4793          * @param {Store} this
4794          * @param {Roo.data.Record[]} records The array of Records added
4795          * @param {Number} index The index at which the record(s) were added
4796          */
4797         add : true,
4798         /**
4799          * @event remove
4800          * Fires when a Record has been removed from the Store
4801          * @param {Store} this
4802          * @param {Roo.data.Record} record The Record that was removed
4803          * @param {Number} index The index at which the record was removed
4804          */
4805         remove : true,
4806         /**
4807          * @event update
4808          * Fires when a Record has been updated
4809          * @param {Store} this
4810          * @param {Roo.data.Record} record The Record that was updated
4811          * @param {String} operation The update operation being performed.  Value may be one of:
4812          * <pre><code>
4813  Roo.data.Record.EDIT
4814  Roo.data.Record.REJECT
4815  Roo.data.Record.COMMIT
4816          * </code></pre>
4817          */
4818         update : true,
4819         /**
4820          * @event clear
4821          * Fires when the data cache has been cleared.
4822          * @param {Store} this
4823          */
4824         clear : true,
4825         /**
4826          * @event beforeload
4827          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4828          * the load action will be canceled.
4829          * @param {Store} this
4830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4831          */
4832         beforeload : true,
4833         /**
4834          * @event beforeloadadd
4835          * Fires after a new set of Records has been loaded.
4836          * @param {Store} this
4837          * @param {Roo.data.Record[]} records The Records that were loaded
4838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4839          */
4840         beforeloadadd : true,
4841         /**
4842          * @event load
4843          * Fires after a new set of Records has been loaded, before they are added to the store.
4844          * @param {Store} this
4845          * @param {Roo.data.Record[]} records The Records that were loaded
4846          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4847          * @params {Object} return from reader
4848          */
4849         load : true,
4850         /**
4851          * @event loadexception
4852          * Fires if an exception occurs in the Proxy during loading.
4853          * Called with the signature of the Proxy's "loadexception" event.
4854          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4855          * 
4856          * @param {Proxy} 
4857          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4858          * @param {Object} load options 
4859          * @param {Object} jsonData from your request (normally this contains the Exception)
4860          */
4861         loadexception : true
4862     });
4863     
4864     if(this.proxy){
4865         this.proxy = Roo.factory(this.proxy, Roo.data);
4866         this.proxy.xmodule = this.xmodule || false;
4867         this.relayEvents(this.proxy,  ["loadexception"]);
4868     }
4869     this.sortToggle = {};
4870     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4871
4872     Roo.data.Store.superclass.constructor.call(this);
4873
4874     if(this.inlineData){
4875         this.loadData(this.inlineData);
4876         delete this.inlineData;
4877     }
4878 };
4879
4880 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4881      /**
4882     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4883     * without a remote query - used by combo/forms at present.
4884     */
4885     
4886     /**
4887     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4888     */
4889     /**
4890     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4891     */
4892     /**
4893     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4894     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4895     */
4896     /**
4897     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4898     * on any HTTP request
4899     */
4900     /**
4901     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4902     */
4903     /**
4904     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4905     */
4906     multiSort: false,
4907     /**
4908     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4909     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4910     */
4911     remoteSort : false,
4912
4913     /**
4914     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4915      * loaded or when a record is removed. (defaults to false).
4916     */
4917     pruneModifiedRecords : false,
4918
4919     // private
4920     lastOptions : null,
4921
4922     /**
4923      * Add Records to the Store and fires the add event.
4924      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4925      */
4926     add : function(records){
4927         records = [].concat(records);
4928         for(var i = 0, len = records.length; i < len; i++){
4929             records[i].join(this);
4930         }
4931         var index = this.data.length;
4932         this.data.addAll(records);
4933         this.fireEvent("add", this, records, index);
4934     },
4935
4936     /**
4937      * Remove a Record from the Store and fires the remove event.
4938      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4939      */
4940     remove : function(record){
4941         var index = this.data.indexOf(record);
4942         this.data.removeAt(index);
4943         if(this.pruneModifiedRecords){
4944             this.modified.remove(record);
4945         }
4946         this.fireEvent("remove", this, record, index);
4947     },
4948
4949     /**
4950      * Remove all Records from the Store and fires the clear event.
4951      */
4952     removeAll : function(){
4953         this.data.clear();
4954         if(this.pruneModifiedRecords){
4955             this.modified = [];
4956         }
4957         this.fireEvent("clear", this);
4958     },
4959
4960     /**
4961      * Inserts Records to the Store at the given index and fires the add event.
4962      * @param {Number} index The start index at which to insert the passed Records.
4963      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4964      */
4965     insert : function(index, records){
4966         records = [].concat(records);
4967         for(var i = 0, len = records.length; i < len; i++){
4968             this.data.insert(index, records[i]);
4969             records[i].join(this);
4970         }
4971         this.fireEvent("add", this, records, index);
4972     },
4973
4974     /**
4975      * Get the index within the cache of the passed Record.
4976      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4977      * @return {Number} The index of the passed Record. Returns -1 if not found.
4978      */
4979     indexOf : function(record){
4980         return this.data.indexOf(record);
4981     },
4982
4983     /**
4984      * Get the index within the cache of the Record with the passed id.
4985      * @param {String} id The id of the Record to find.
4986      * @return {Number} The index of the Record. Returns -1 if not found.
4987      */
4988     indexOfId : function(id){
4989         return this.data.indexOfKey(id);
4990     },
4991
4992     /**
4993      * Get the Record with the specified id.
4994      * @param {String} id The id of the Record to find.
4995      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4996      */
4997     getById : function(id){
4998         return this.data.key(id);
4999     },
5000
5001     /**
5002      * Get the Record at the specified index.
5003      * @param {Number} index The index of the Record to find.
5004      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5005      */
5006     getAt : function(index){
5007         return this.data.itemAt(index);
5008     },
5009
5010     /**
5011      * Returns a range of Records between specified indices.
5012      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5013      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5014      * @return {Roo.data.Record[]} An array of Records
5015      */
5016     getRange : function(start, end){
5017         return this.data.getRange(start, end);
5018     },
5019
5020     // private
5021     storeOptions : function(o){
5022         o = Roo.apply({}, o);
5023         delete o.callback;
5024         delete o.scope;
5025         this.lastOptions = o;
5026     },
5027
5028     /**
5029      * Loads the Record cache from the configured Proxy using the configured Reader.
5030      * <p>
5031      * If using remote paging, then the first load call must specify the <em>start</em>
5032      * and <em>limit</em> properties in the options.params property to establish the initial
5033      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5034      * <p>
5035      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5036      * and this call will return before the new data has been loaded. Perform any post-processing
5037      * in a callback function, or in a "load" event handler.</strong>
5038      * <p>
5039      * @param {Object} options An object containing properties which control loading options:<ul>
5040      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5041      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5042      * passed the following arguments:<ul>
5043      * <li>r : Roo.data.Record[]</li>
5044      * <li>options: Options object from the load call</li>
5045      * <li>success: Boolean success indicator</li></ul></li>
5046      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5047      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5048      * </ul>
5049      */
5050     load : function(options){
5051         options = options || {};
5052         if(this.fireEvent("beforeload", this, options) !== false){
5053             this.storeOptions(options);
5054             var p = Roo.apply(options.params || {}, this.baseParams);
5055             // if meta was not loaded from remote source.. try requesting it.
5056             if (!this.reader.metaFromRemote) {
5057                 p._requestMeta = 1;
5058             }
5059             if(this.sortInfo && this.remoteSort){
5060                 var pn = this.paramNames;
5061                 p[pn["sort"]] = this.sortInfo.field;
5062                 p[pn["dir"]] = this.sortInfo.direction;
5063             }
5064             if (this.multiSort) {
5065                 var pn = this.paramNames;
5066                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5067             }
5068             
5069             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5070         }
5071     },
5072
5073     /**
5074      * Reloads the Record cache from the configured Proxy using the configured Reader and
5075      * the options from the last load operation performed.
5076      * @param {Object} options (optional) An object containing properties which may override the options
5077      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5078      * the most recently used options are reused).
5079      */
5080     reload : function(options){
5081         this.load(Roo.applyIf(options||{}, this.lastOptions));
5082     },
5083
5084     // private
5085     // Called as a callback by the Reader during a load operation.
5086     loadRecords : function(o, options, success){
5087         if(!o || success === false){
5088             if(success !== false){
5089                 this.fireEvent("load", this, [], options, o);
5090             }
5091             if(options.callback){
5092                 options.callback.call(options.scope || this, [], options, false);
5093             }
5094             return;
5095         }
5096         // if data returned failure - throw an exception.
5097         if (o.success === false) {
5098             // show a message if no listener is registered.
5099             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5100                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5101             }
5102             // loadmask wil be hooked into this..
5103             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5104             return;
5105         }
5106         var r = o.records, t = o.totalRecords || r.length;
5107         
5108         this.fireEvent("beforeloadadd", this, r, options, o);
5109         
5110         if(!options || options.add !== true){
5111             if(this.pruneModifiedRecords){
5112                 this.modified = [];
5113             }
5114             for(var i = 0, len = r.length; i < len; i++){
5115                 r[i].join(this);
5116             }
5117             if(this.snapshot){
5118                 this.data = this.snapshot;
5119                 delete this.snapshot;
5120             }
5121             this.data.clear();
5122             this.data.addAll(r);
5123             this.totalLength = t;
5124             this.applySort();
5125             this.fireEvent("datachanged", this);
5126         }else{
5127             this.totalLength = Math.max(t, this.data.length+r.length);
5128             this.add(r);
5129         }
5130         this.fireEvent("load", this, r, options, o);
5131         if(options.callback){
5132             options.callback.call(options.scope || this, r, options, true);
5133         }
5134     },
5135
5136
5137     /**
5138      * Loads data from a passed data block. A Reader which understands the format of the data
5139      * must have been configured in the constructor.
5140      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5141      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5142      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5143      */
5144     loadData : function(o, append){
5145         var r = this.reader.readRecords(o);
5146         this.loadRecords(r, {add: append}, true);
5147     },
5148
5149     /**
5150      * Gets the number of cached records.
5151      * <p>
5152      * <em>If using paging, this may not be the total size of the dataset. If the data object
5153      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5154      * the data set size</em>
5155      */
5156     getCount : function(){
5157         return this.data.length || 0;
5158     },
5159
5160     /**
5161      * Gets the total number of records in the dataset as returned by the server.
5162      * <p>
5163      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5164      * the dataset size</em>
5165      */
5166     getTotalCount : function(){
5167         return this.totalLength || 0;
5168     },
5169
5170     /**
5171      * Returns the sort state of the Store as an object with two properties:
5172      * <pre><code>
5173  field {String} The name of the field by which the Records are sorted
5174  direction {String} The sort order, "ASC" or "DESC"
5175      * </code></pre>
5176      */
5177     getSortState : function(){
5178         return this.sortInfo;
5179     },
5180
5181     // private
5182     applySort : function(){
5183         if(this.sortInfo && !this.remoteSort){
5184             var s = this.sortInfo, f = s.field;
5185             var st = this.fields.get(f).sortType;
5186             var fn = function(r1, r2){
5187                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5188                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5189             };
5190             this.data.sort(s.direction, fn);
5191             if(this.snapshot && this.snapshot != this.data){
5192                 this.snapshot.sort(s.direction, fn);
5193             }
5194         }
5195     },
5196
5197     /**
5198      * Sets the default sort column and order to be used by the next load operation.
5199      * @param {String} fieldName The name of the field to sort by.
5200      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5201      */
5202     setDefaultSort : function(field, dir){
5203         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5204     },
5205
5206     /**
5207      * Sort the Records.
5208      * If remote sorting is used, the sort is performed on the server, and the cache is
5209      * reloaded. If local sorting is used, the cache is sorted internally.
5210      * @param {String} fieldName The name of the field to sort by.
5211      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5212      */
5213     sort : function(fieldName, dir){
5214         var f = this.fields.get(fieldName);
5215         if(!dir){
5216             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5217             
5218             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5219                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5220             }else{
5221                 dir = f.sortDir;
5222             }
5223         }
5224         this.sortToggle[f.name] = dir;
5225         this.sortInfo = {field: f.name, direction: dir};
5226         if(!this.remoteSort){
5227             this.applySort();
5228             this.fireEvent("datachanged", this);
5229         }else{
5230             this.load(this.lastOptions);
5231         }
5232     },
5233
5234     /**
5235      * Calls the specified function for each of the Records in the cache.
5236      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5237      * Returning <em>false</em> aborts and exits the iteration.
5238      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5239      */
5240     each : function(fn, scope){
5241         this.data.each(fn, scope);
5242     },
5243
5244     /**
5245      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5246      * (e.g., during paging).
5247      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5248      */
5249     getModifiedRecords : function(){
5250         return this.modified;
5251     },
5252
5253     // private
5254     createFilterFn : function(property, value, anyMatch){
5255         if(!value.exec){ // not a regex
5256             value = String(value);
5257             if(value.length == 0){
5258                 return false;
5259             }
5260             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5261         }
5262         return function(r){
5263             return value.test(r.data[property]);
5264         };
5265     },
5266
5267     /**
5268      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5269      * @param {String} property A field on your records
5270      * @param {Number} start The record index to start at (defaults to 0)
5271      * @param {Number} end The last record index to include (defaults to length - 1)
5272      * @return {Number} The sum
5273      */
5274     sum : function(property, start, end){
5275         var rs = this.data.items, v = 0;
5276         start = start || 0;
5277         end = (end || end === 0) ? end : rs.length-1;
5278
5279         for(var i = start; i <= end; i++){
5280             v += (rs[i].data[property] || 0);
5281         }
5282         return v;
5283     },
5284
5285     /**
5286      * Filter the records by a specified property.
5287      * @param {String} field A field on your records
5288      * @param {String/RegExp} value Either a string that the field
5289      * should start with or a RegExp to test against the field
5290      * @param {Boolean} anyMatch True to match any part not just the beginning
5291      */
5292     filter : function(property, value, anyMatch){
5293         var fn = this.createFilterFn(property, value, anyMatch);
5294         return fn ? this.filterBy(fn) : this.clearFilter();
5295     },
5296
5297     /**
5298      * Filter by a function. The specified function will be called with each
5299      * record in this data source. If the function returns true the record is included,
5300      * otherwise it is filtered.
5301      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5302      * @param {Object} scope (optional) The scope of the function (defaults to this)
5303      */
5304     filterBy : function(fn, scope){
5305         this.snapshot = this.snapshot || this.data;
5306         this.data = this.queryBy(fn, scope||this);
5307         this.fireEvent("datachanged", this);
5308     },
5309
5310     /**
5311      * Query the records by a specified property.
5312      * @param {String} field A field on your records
5313      * @param {String/RegExp} value Either a string that the field
5314      * should start with or a RegExp to test against the field
5315      * @param {Boolean} anyMatch True to match any part not just the beginning
5316      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5317      */
5318     query : function(property, value, anyMatch){
5319         var fn = this.createFilterFn(property, value, anyMatch);
5320         return fn ? this.queryBy(fn) : this.data.clone();
5321     },
5322
5323     /**
5324      * Query by a function. The specified function will be called with each
5325      * record in this data source. If the function returns true the record is included
5326      * in the results.
5327      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5328      * @param {Object} scope (optional) The scope of the function (defaults to this)
5329       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5330      **/
5331     queryBy : function(fn, scope){
5332         var data = this.snapshot || this.data;
5333         return data.filterBy(fn, scope||this);
5334     },
5335
5336     /**
5337      * Collects unique values for a particular dataIndex from this store.
5338      * @param {String} dataIndex The property to collect
5339      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5340      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5341      * @return {Array} An array of the unique values
5342      **/
5343     collect : function(dataIndex, allowNull, bypassFilter){
5344         var d = (bypassFilter === true && this.snapshot) ?
5345                 this.snapshot.items : this.data.items;
5346         var v, sv, r = [], l = {};
5347         for(var i = 0, len = d.length; i < len; i++){
5348             v = d[i].data[dataIndex];
5349             sv = String(v);
5350             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5351                 l[sv] = true;
5352                 r[r.length] = v;
5353             }
5354         }
5355         return r;
5356     },
5357
5358     /**
5359      * Revert to a view of the Record cache with no filtering applied.
5360      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5361      */
5362     clearFilter : function(suppressEvent){
5363         if(this.snapshot && this.snapshot != this.data){
5364             this.data = this.snapshot;
5365             delete this.snapshot;
5366             if(suppressEvent !== true){
5367                 this.fireEvent("datachanged", this);
5368             }
5369         }
5370     },
5371
5372     // private
5373     afterEdit : function(record){
5374         if(this.modified.indexOf(record) == -1){
5375             this.modified.push(record);
5376         }
5377         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5378     },
5379     
5380     // private
5381     afterReject : function(record){
5382         this.modified.remove(record);
5383         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5384     },
5385
5386     // private
5387     afterCommit : function(record){
5388         this.modified.remove(record);
5389         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5390     },
5391
5392     /**
5393      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5394      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5395      */
5396     commitChanges : function(){
5397         var m = this.modified.slice(0);
5398         this.modified = [];
5399         for(var i = 0, len = m.length; i < len; i++){
5400             m[i].commit();
5401         }
5402     },
5403
5404     /**
5405      * Cancel outstanding changes on all changed records.
5406      */
5407     rejectChanges : function(){
5408         var m = this.modified.slice(0);
5409         this.modified = [];
5410         for(var i = 0, len = m.length; i < len; i++){
5411             m[i].reject();
5412         }
5413     },
5414
5415     onMetaChange : function(meta, rtype, o){
5416         this.recordType = rtype;
5417         this.fields = rtype.prototype.fields;
5418         delete this.snapshot;
5419         this.sortInfo = meta.sortInfo || this.sortInfo;
5420         this.modified = [];
5421         this.fireEvent('metachange', this, this.reader.meta);
5422     },
5423     
5424     moveIndex : function(data, type)
5425     {
5426         var index = this.indexOf(data);
5427         
5428         var newIndex = index + type;
5429         
5430         this.remove(data);
5431         
5432         this.insert(newIndex, data);
5433         
5434     }
5435 });/*
5436  * Based on:
5437  * Ext JS Library 1.1.1
5438  * Copyright(c) 2006-2007, Ext JS, LLC.
5439  *
5440  * Originally Released Under LGPL - original licence link has changed is not relivant.
5441  *
5442  * Fork - LGPL
5443  * <script type="text/javascript">
5444  */
5445
5446 /**
5447  * @class Roo.data.SimpleStore
5448  * @extends Roo.data.Store
5449  * Small helper class to make creating Stores from Array data easier.
5450  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5451  * @cfg {Array} fields An array of field definition objects, or field name strings.
5452  * @cfg {Array} data The multi-dimensional array of data
5453  * @constructor
5454  * @param {Object} config
5455  */
5456 Roo.data.SimpleStore = function(config){
5457     Roo.data.SimpleStore.superclass.constructor.call(this, {
5458         isLocal : true,
5459         reader: new Roo.data.ArrayReader({
5460                 id: config.id
5461             },
5462             Roo.data.Record.create(config.fields)
5463         ),
5464         proxy : new Roo.data.MemoryProxy(config.data)
5465     });
5466     this.load();
5467 };
5468 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478
5479 /**
5480 /**
5481  * @extends Roo.data.Store
5482  * @class Roo.data.JsonStore
5483  * Small helper class to make creating Stores for JSON data easier. <br/>
5484 <pre><code>
5485 var store = new Roo.data.JsonStore({
5486     url: 'get-images.php',
5487     root: 'images',
5488     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5489 });
5490 </code></pre>
5491  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5492  * JsonReader and HttpProxy (unless inline data is provided).</b>
5493  * @cfg {Array} fields An array of field definition objects, or field name strings.
5494  * @constructor
5495  * @param {Object} config
5496  */
5497 Roo.data.JsonStore = function(c){
5498     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5499         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5500         reader: new Roo.data.JsonReader(c, c.fields)
5501     }));
5502 };
5503 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514  
5515 Roo.data.Field = function(config){
5516     if(typeof config == "string"){
5517         config = {name: config};
5518     }
5519     Roo.apply(this, config);
5520     
5521     if(!this.type){
5522         this.type = "auto";
5523     }
5524     
5525     var st = Roo.data.SortTypes;
5526     // named sortTypes are supported, here we look them up
5527     if(typeof this.sortType == "string"){
5528         this.sortType = st[this.sortType];
5529     }
5530     
5531     // set default sortType for strings and dates
5532     if(!this.sortType){
5533         switch(this.type){
5534             case "string":
5535                 this.sortType = st.asUCString;
5536                 break;
5537             case "date":
5538                 this.sortType = st.asDate;
5539                 break;
5540             default:
5541                 this.sortType = st.none;
5542         }
5543     }
5544
5545     // define once
5546     var stripRe = /[\$,%]/g;
5547
5548     // prebuilt conversion function for this field, instead of
5549     // switching every time we're reading a value
5550     if(!this.convert){
5551         var cv, dateFormat = this.dateFormat;
5552         switch(this.type){
5553             case "":
5554             case "auto":
5555             case undefined:
5556                 cv = function(v){ return v; };
5557                 break;
5558             case "string":
5559                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5560                 break;
5561             case "int":
5562                 cv = function(v){
5563                     return v !== undefined && v !== null && v !== '' ?
5564                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5565                     };
5566                 break;
5567             case "float":
5568                 cv = function(v){
5569                     return v !== undefined && v !== null && v !== '' ?
5570                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5571                     };
5572                 break;
5573             case "bool":
5574             case "boolean":
5575                 cv = function(v){ return v === true || v === "true" || v == 1; };
5576                 break;
5577             case "date":
5578                 cv = function(v){
5579                     if(!v){
5580                         return '';
5581                     }
5582                     if(v instanceof Date){
5583                         return v;
5584                     }
5585                     if(dateFormat){
5586                         if(dateFormat == "timestamp"){
5587                             return new Date(v*1000);
5588                         }
5589                         return Date.parseDate(v, dateFormat);
5590                     }
5591                     var parsed = Date.parse(v);
5592                     return parsed ? new Date(parsed) : null;
5593                 };
5594              break;
5595             
5596         }
5597         this.convert = cv;
5598     }
5599 };
5600
5601 Roo.data.Field.prototype = {
5602     dateFormat: null,
5603     defaultValue: "",
5604     mapping: null,
5605     sortType : null,
5606     sortDir : "ASC"
5607 };/*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 // Base class for reading structured data from a data source.  This class is intended to be
5619 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5620
5621 /**
5622  * @class Roo.data.DataReader
5623  * Base class for reading structured data from a data source.  This class is intended to be
5624  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5625  */
5626
5627 Roo.data.DataReader = function(meta, recordType){
5628     
5629     this.meta = meta;
5630     
5631     this.recordType = recordType instanceof Array ? 
5632         Roo.data.Record.create(recordType) : recordType;
5633 };
5634
5635 Roo.data.DataReader.prototype = {
5636      /**
5637      * Create an empty record
5638      * @param {Object} data (optional) - overlay some values
5639      * @return {Roo.data.Record} record created.
5640      */
5641     newRow :  function(d) {
5642         var da =  {};
5643         this.recordType.prototype.fields.each(function(c) {
5644             switch( c.type) {
5645                 case 'int' : da[c.name] = 0; break;
5646                 case 'date' : da[c.name] = new Date(); break;
5647                 case 'float' : da[c.name] = 0.0; break;
5648                 case 'boolean' : da[c.name] = false; break;
5649                 default : da[c.name] = ""; break;
5650             }
5651             
5652         });
5653         return new this.recordType(Roo.apply(da, d));
5654     }
5655     
5656 };/*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.data.DataProxy
5669  * @extends Roo.data.Observable
5670  * This class is an abstract base class for implementations which provide retrieval of
5671  * unformatted data objects.<br>
5672  * <p>
5673  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5674  * (of the appropriate type which knows how to parse the data object) to provide a block of
5675  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5676  * <p>
5677  * Custom implementations must implement the load method as described in
5678  * {@link Roo.data.HttpProxy#load}.
5679  */
5680 Roo.data.DataProxy = function(){
5681     this.addEvents({
5682         /**
5683          * @event beforeload
5684          * Fires before a network request is made to retrieve a data object.
5685          * @param {Object} This DataProxy object.
5686          * @param {Object} params The params parameter to the load function.
5687          */
5688         beforeload : true,
5689         /**
5690          * @event load
5691          * Fires before the load method's callback is called.
5692          * @param {Object} This DataProxy object.
5693          * @param {Object} o The data object.
5694          * @param {Object} arg The callback argument object passed to the load function.
5695          */
5696         load : true,
5697         /**
5698          * @event loadexception
5699          * Fires if an Exception occurs during data retrieval.
5700          * @param {Object} This DataProxy object.
5701          * @param {Object} o The data object.
5702          * @param {Object} arg The callback argument object passed to the load function.
5703          * @param {Object} e The Exception.
5704          */
5705         loadexception : true
5706     });
5707     Roo.data.DataProxy.superclass.constructor.call(this);
5708 };
5709
5710 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5711
5712     /**
5713      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5714      */
5715 /*
5716  * Based on:
5717  * Ext JS Library 1.1.1
5718  * Copyright(c) 2006-2007, Ext JS, LLC.
5719  *
5720  * Originally Released Under LGPL - original licence link has changed is not relivant.
5721  *
5722  * Fork - LGPL
5723  * <script type="text/javascript">
5724  */
5725 /**
5726  * @class Roo.data.MemoryProxy
5727  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5728  * to the Reader when its load method is called.
5729  * @constructor
5730  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5731  */
5732 Roo.data.MemoryProxy = function(data){
5733     if (data.data) {
5734         data = data.data;
5735     }
5736     Roo.data.MemoryProxy.superclass.constructor.call(this);
5737     this.data = data;
5738 };
5739
5740 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5741     /**
5742      * Load data from the requested source (in this case an in-memory
5743      * data object passed to the constructor), read the data object into
5744      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5745      * process that block using the passed callback.
5746      * @param {Object} params This parameter is not used by the MemoryProxy class.
5747      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5748      * object into a block of Roo.data.Records.
5749      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5750      * The function must be passed <ul>
5751      * <li>The Record block object</li>
5752      * <li>The "arg" argument from the load function</li>
5753      * <li>A boolean success indicator</li>
5754      * </ul>
5755      * @param {Object} scope The scope in which to call the callback
5756      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5757      */
5758     load : function(params, reader, callback, scope, arg){
5759         params = params || {};
5760         var result;
5761         try {
5762             result = reader.readRecords(this.data);
5763         }catch(e){
5764             this.fireEvent("loadexception", this, arg, null, e);
5765             callback.call(scope, null, arg, false);
5766             return;
5767         }
5768         callback.call(scope, result, arg, true);
5769     },
5770     
5771     // private
5772     update : function(params, records){
5773         
5774     }
5775 });/*
5776  * Based on:
5777  * Ext JS Library 1.1.1
5778  * Copyright(c) 2006-2007, Ext JS, LLC.
5779  *
5780  * Originally Released Under LGPL - original licence link has changed is not relivant.
5781  *
5782  * Fork - LGPL
5783  * <script type="text/javascript">
5784  */
5785 /**
5786  * @class Roo.data.HttpProxy
5787  * @extends Roo.data.DataProxy
5788  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5789  * configured to reference a certain URL.<br><br>
5790  * <p>
5791  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5792  * from which the running page was served.<br><br>
5793  * <p>
5794  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5795  * <p>
5796  * Be aware that to enable the browser to parse an XML document, the server must set
5797  * the Content-Type header in the HTTP response to "text/xml".
5798  * @constructor
5799  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5800  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5801  * will be used to make the request.
5802  */
5803 Roo.data.HttpProxy = function(conn){
5804     Roo.data.HttpProxy.superclass.constructor.call(this);
5805     // is conn a conn config or a real conn?
5806     this.conn = conn;
5807     this.useAjax = !conn || !conn.events;
5808   
5809 };
5810
5811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5812     // thse are take from connection...
5813     
5814     /**
5815      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5816      */
5817     /**
5818      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5819      * extra parameters to each request made by this object. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5823      *  to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5827      */
5828     /**
5829      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5830      */
5831      /**
5832      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5833      * @type Boolean
5834      */
5835   
5836
5837     /**
5838      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5839      * @type Boolean
5840      */
5841     /**
5842      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5843      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5844      * a finer-grained basis than the DataProxy events.
5845      */
5846     getConnection : function(){
5847         return this.useAjax ? Roo.Ajax : this.conn;
5848     },
5849
5850     /**
5851      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5852      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5853      * process that block using the passed callback.
5854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5855      * for the request to the remote server.
5856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5857      * object into a block of Roo.data.Records.
5858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5859      * The function must be passed <ul>
5860      * <li>The Record block object</li>
5861      * <li>The "arg" argument from the load function</li>
5862      * <li>A boolean success indicator</li>
5863      * </ul>
5864      * @param {Object} scope The scope in which to call the callback
5865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5866      */
5867     load : function(params, reader, callback, scope, arg){
5868         if(this.fireEvent("beforeload", this, params) !== false){
5869             var  o = {
5870                 params : params || {},
5871                 request: {
5872                     callback : callback,
5873                     scope : scope,
5874                     arg : arg
5875                 },
5876                 reader: reader,
5877                 callback : this.loadResponse,
5878                 scope: this
5879             };
5880             if(this.useAjax){
5881                 Roo.applyIf(o, this.conn);
5882                 if(this.activeRequest){
5883                     Roo.Ajax.abort(this.activeRequest);
5884                 }
5885                 this.activeRequest = Roo.Ajax.request(o);
5886             }else{
5887                 this.conn.request(o);
5888             }
5889         }else{
5890             callback.call(scope||this, null, arg, false);
5891         }
5892     },
5893
5894     // private
5895     loadResponse : function(o, success, response){
5896         delete this.activeRequest;
5897         if(!success){
5898             this.fireEvent("loadexception", this, o, response);
5899             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5900             return;
5901         }
5902         var result;
5903         try {
5904             result = o.reader.read(response);
5905         }catch(e){
5906             this.fireEvent("loadexception", this, o, response, e);
5907             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5908             return;
5909         }
5910         
5911         this.fireEvent("load", this, o, o.request.arg);
5912         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5913     },
5914
5915     // private
5916     update : function(dataSet){
5917
5918     },
5919
5920     // private
5921     updateResponse : function(dataSet){
5922
5923     }
5924 });/*
5925  * Based on:
5926  * Ext JS Library 1.1.1
5927  * Copyright(c) 2006-2007, Ext JS, LLC.
5928  *
5929  * Originally Released Under LGPL - original licence link has changed is not relivant.
5930  *
5931  * Fork - LGPL
5932  * <script type="text/javascript">
5933  */
5934
5935 /**
5936  * @class Roo.data.ScriptTagProxy
5937  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5938  * other than the originating domain of the running page.<br><br>
5939  * <p>
5940  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5941  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5942  * <p>
5943  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5944  * source code that is used as the source inside a &lt;script> tag.<br><br>
5945  * <p>
5946  * In order for the browser to process the returned data, the server must wrap the data object
5947  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5948  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5949  * depending on whether the callback name was passed:
5950  * <p>
5951  * <pre><code>
5952 boolean scriptTag = false;
5953 String cb = request.getParameter("callback");
5954 if (cb != null) {
5955     scriptTag = true;
5956     response.setContentType("text/javascript");
5957 } else {
5958     response.setContentType("application/x-json");
5959 }
5960 Writer out = response.getWriter();
5961 if (scriptTag) {
5962     out.write(cb + "(");
5963 }
5964 out.print(dataBlock.toJsonString());
5965 if (scriptTag) {
5966     out.write(");");
5967 }
5968 </pre></code>
5969  *
5970  * @constructor
5971  * @param {Object} config A configuration object.
5972  */
5973 Roo.data.ScriptTagProxy = function(config){
5974     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5975     Roo.apply(this, config);
5976     this.head = document.getElementsByTagName("head")[0];
5977 };
5978
5979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5980
5981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5982     /**
5983      * @cfg {String} url The URL from which to request the data object.
5984      */
5985     /**
5986      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5987      */
5988     timeout : 30000,
5989     /**
5990      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5991      * the server the name of the callback function set up by the load call to process the returned data object.
5992      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5993      * javascript output which calls this named function passing the data object as its only parameter.
5994      */
5995     callbackParam : "callback",
5996     /**
5997      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5998      * name to the request.
5999      */
6000     nocache : true,
6001
6002     /**
6003      * Load data from the configured URL, read the data object into
6004      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6005      * process that block using the passed callback.
6006      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6007      * for the request to the remote server.
6008      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6009      * object into a block of Roo.data.Records.
6010      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6011      * The function must be passed <ul>
6012      * <li>The Record block object</li>
6013      * <li>The "arg" argument from the load function</li>
6014      * <li>A boolean success indicator</li>
6015      * </ul>
6016      * @param {Object} scope The scope in which to call the callback
6017      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6018      */
6019     load : function(params, reader, callback, scope, arg){
6020         if(this.fireEvent("beforeload", this, params) !== false){
6021
6022             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6023
6024             var url = this.url;
6025             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6026             if(this.nocache){
6027                 url += "&_dc=" + (new Date().getTime());
6028             }
6029             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6030             var trans = {
6031                 id : transId,
6032                 cb : "stcCallback"+transId,
6033                 scriptId : "stcScript"+transId,
6034                 params : params,
6035                 arg : arg,
6036                 url : url,
6037                 callback : callback,
6038                 scope : scope,
6039                 reader : reader
6040             };
6041             var conn = this;
6042
6043             window[trans.cb] = function(o){
6044                 conn.handleResponse(o, trans);
6045             };
6046
6047             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6048
6049             if(this.autoAbort !== false){
6050                 this.abort();
6051             }
6052
6053             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6054
6055             var script = document.createElement("script");
6056             script.setAttribute("src", url);
6057             script.setAttribute("type", "text/javascript");
6058             script.setAttribute("id", trans.scriptId);
6059             this.head.appendChild(script);
6060
6061             this.trans = trans;
6062         }else{
6063             callback.call(scope||this, null, arg, false);
6064         }
6065     },
6066
6067     // private
6068     isLoading : function(){
6069         return this.trans ? true : false;
6070     },
6071
6072     /**
6073      * Abort the current server request.
6074      */
6075     abort : function(){
6076         if(this.isLoading()){
6077             this.destroyTrans(this.trans);
6078         }
6079     },
6080
6081     // private
6082     destroyTrans : function(trans, isLoaded){
6083         this.head.removeChild(document.getElementById(trans.scriptId));
6084         clearTimeout(trans.timeoutId);
6085         if(isLoaded){
6086             window[trans.cb] = undefined;
6087             try{
6088                 delete window[trans.cb];
6089             }catch(e){}
6090         }else{
6091             // if hasn't been loaded, wait for load to remove it to prevent script error
6092             window[trans.cb] = function(){
6093                 window[trans.cb] = undefined;
6094                 try{
6095                     delete window[trans.cb];
6096                 }catch(e){}
6097             };
6098         }
6099     },
6100
6101     // private
6102     handleResponse : function(o, trans){
6103         this.trans = false;
6104         this.destroyTrans(trans, true);
6105         var result;
6106         try {
6107             result = trans.reader.readRecords(o);
6108         }catch(e){
6109             this.fireEvent("loadexception", this, o, trans.arg, e);
6110             trans.callback.call(trans.scope||window, null, trans.arg, false);
6111             return;
6112         }
6113         this.fireEvent("load", this, o, trans.arg);
6114         trans.callback.call(trans.scope||window, result, trans.arg, true);
6115     },
6116
6117     // private
6118     handleFailure : function(trans){
6119         this.trans = false;
6120         this.destroyTrans(trans, false);
6121         this.fireEvent("loadexception", this, null, trans.arg);
6122         trans.callback.call(trans.scope||window, null, trans.arg, false);
6123     }
6124 });/*
6125  * Based on:
6126  * Ext JS Library 1.1.1
6127  * Copyright(c) 2006-2007, Ext JS, LLC.
6128  *
6129  * Originally Released Under LGPL - original licence link has changed is not relivant.
6130  *
6131  * Fork - LGPL
6132  * <script type="text/javascript">
6133  */
6134
6135 /**
6136  * @class Roo.data.JsonReader
6137  * @extends Roo.data.DataReader
6138  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6139  * based on mappings in a provided Roo.data.Record constructor.
6140  * 
6141  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6142  * in the reply previously. 
6143  * 
6144  * <p>
6145  * Example code:
6146  * <pre><code>
6147 var RecordDef = Roo.data.Record.create([
6148     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6149     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6150 ]);
6151 var myReader = new Roo.data.JsonReader({
6152     totalProperty: "results",    // The property which contains the total dataset size (optional)
6153     root: "rows",                // The property which contains an Array of row objects
6154     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6155 }, RecordDef);
6156 </code></pre>
6157  * <p>
6158  * This would consume a JSON file like this:
6159  * <pre><code>
6160 { 'results': 2, 'rows': [
6161     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6162     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6163 }
6164 </code></pre>
6165  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6166  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6167  * paged from the remote server.
6168  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6169  * @cfg {String} root name of the property which contains the Array of row objects.
6170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6171  * @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              cmenu.menu.items.push({
27365                 actiontype : 'tidy',
27366                 html: 'Tidy HTML Source',
27367                 handler: function(a,b) {
27368                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27369                     editorcore.syncValue();
27370                 },
27371                 tabIndex:-1
27372             });
27373             
27374             
27375             tb.add(cmenu);
27376         }
27377          
27378         if (!this.disable.specialElements) {
27379             var semenu = {
27380                 text: "Other;",
27381                 cls: 'x-edit-none',
27382                 menu : {
27383                     items : []
27384                 }
27385             };
27386             for (var i =0; i < this.specialElements.length; i++) {
27387                 semenu.menu.items.push(
27388                     Roo.apply({ 
27389                         handler: function(a,b) {
27390                             editor.insertAtCursor(this.ihtml);
27391                         }
27392                     }, this.specialElements[i])
27393                 );
27394                     
27395             }
27396             
27397             tb.add(semenu);
27398             
27399             
27400         }
27401          
27402         
27403         if (this.btns) {
27404             for(var i =0; i< this.btns.length;i++) {
27405                 var b = Roo.factory(this.btns[i],Roo.form);
27406                 b.cls =  'x-edit-none';
27407                 
27408                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27409                     b.cls += ' x-init-enable';
27410                 }
27411                 
27412                 b.scope = editorcore;
27413                 tb.add(b);
27414             }
27415         
27416         }
27417         
27418         
27419         
27420         // disable everything...
27421         
27422         this.tb.items.each(function(item){
27423             
27424            if(
27425                 item.id != editorcore.frameId+ '-sourceedit' && 
27426                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27427             ){
27428                 
27429                 item.disable();
27430             }
27431         });
27432         this.rendered = true;
27433         
27434         // the all the btns;
27435         editor.on('editorevent', this.updateToolbar, this);
27436         // other toolbars need to implement this..
27437         //editor.on('editmodechange', this.updateToolbar, this);
27438     },
27439     
27440     
27441     relayBtnCmd : function(btn) {
27442         this.editorcore.relayCmd(btn.cmd);
27443     },
27444     // private used internally
27445     createLink : function(){
27446         Roo.log("create link?");
27447         var url = prompt(this.createLinkText, this.defaultLinkValue);
27448         if(url && url != 'http:/'+'/'){
27449             this.editorcore.relayCmd('createlink', url);
27450         }
27451     },
27452
27453     
27454     /**
27455      * Protected method that will not generally be called directly. It triggers
27456      * a toolbar update by reading the markup state of the current selection in the editor.
27457      */
27458     updateToolbar: function(){
27459
27460         if(!this.editorcore.activated){
27461             this.editor.onFirstFocus();
27462             return;
27463         }
27464
27465         var btns = this.tb.items.map, 
27466             doc = this.editorcore.doc,
27467             frameId = this.editorcore.frameId;
27468
27469         if(!this.disable.font && !Roo.isSafari){
27470             /*
27471             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27472             if(name != this.fontSelect.dom.value){
27473                 this.fontSelect.dom.value = name;
27474             }
27475             */
27476         }
27477         if(!this.disable.format){
27478             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27479             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27480             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27481         }
27482         if(!this.disable.alignments){
27483             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27484             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27485             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27486         }
27487         if(!Roo.isSafari && !this.disable.lists){
27488             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27489             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27490         }
27491         
27492         var ans = this.editorcore.getAllAncestors();
27493         if (this.formatCombo) {
27494             
27495             
27496             var store = this.formatCombo.store;
27497             this.formatCombo.setValue("");
27498             for (var i =0; i < ans.length;i++) {
27499                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27500                     // select it..
27501                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27502                     break;
27503                 }
27504             }
27505         }
27506         
27507         
27508         
27509         // hides menus... - so this cant be on a menu...
27510         Roo.menu.MenuMgr.hideAll();
27511
27512         //this.editorsyncValue();
27513     },
27514    
27515     
27516     createFontOptions : function(){
27517         var buf = [], fs = this.fontFamilies, ff, lc;
27518         
27519         
27520         
27521         for(var i = 0, len = fs.length; i< len; i++){
27522             ff = fs[i];
27523             lc = ff.toLowerCase();
27524             buf.push(
27525                 '<option value="',lc,'" style="font-family:',ff,';"',
27526                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27527                     ff,
27528                 '</option>'
27529             );
27530         }
27531         return buf.join('');
27532     },
27533     
27534     toggleSourceEdit : function(sourceEditMode){
27535         
27536         Roo.log("toolbar toogle");
27537         if(sourceEditMode === undefined){
27538             sourceEditMode = !this.sourceEditMode;
27539         }
27540         this.sourceEditMode = sourceEditMode === true;
27541         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27542         // just toggle the button?
27543         if(btn.pressed !== this.sourceEditMode){
27544             btn.toggle(this.sourceEditMode);
27545             return;
27546         }
27547         
27548         if(sourceEditMode){
27549             Roo.log("disabling buttons");
27550             this.tb.items.each(function(item){
27551                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27552                     item.disable();
27553                 }
27554             });
27555           
27556         }else{
27557             Roo.log("enabling buttons");
27558             if(this.editorcore.initialized){
27559                 this.tb.items.each(function(item){
27560                     item.enable();
27561                 });
27562             }
27563             
27564         }
27565         Roo.log("calling toggole on editor");
27566         // tell the editor that it's been pressed..
27567         this.editor.toggleSourceEdit(sourceEditMode);
27568        
27569     },
27570      /**
27571      * Object collection of toolbar tooltips for the buttons in the editor. The key
27572      * is the command id associated with that button and the value is a valid QuickTips object.
27573      * For example:
27574 <pre><code>
27575 {
27576     bold : {
27577         title: 'Bold (Ctrl+B)',
27578         text: 'Make the selected text bold.',
27579         cls: 'x-html-editor-tip'
27580     },
27581     italic : {
27582         title: 'Italic (Ctrl+I)',
27583         text: 'Make the selected text italic.',
27584         cls: 'x-html-editor-tip'
27585     },
27586     ...
27587 </code></pre>
27588     * @type Object
27589      */
27590     buttonTips : {
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         underline : {
27602             title: 'Underline (Ctrl+U)',
27603             text: 'Underline the selected text.',
27604             cls: 'x-html-editor-tip'
27605         },
27606         increasefontsize : {
27607             title: 'Grow Text',
27608             text: 'Increase the font size.',
27609             cls: 'x-html-editor-tip'
27610         },
27611         decreasefontsize : {
27612             title: 'Shrink Text',
27613             text: 'Decrease the font size.',
27614             cls: 'x-html-editor-tip'
27615         },
27616         backcolor : {
27617             title: 'Text Highlight Color',
27618             text: 'Change the background color of the selected text.',
27619             cls: 'x-html-editor-tip'
27620         },
27621         forecolor : {
27622             title: 'Font Color',
27623             text: 'Change the color of the selected text.',
27624             cls: 'x-html-editor-tip'
27625         },
27626         justifyleft : {
27627             title: 'Align Text Left',
27628             text: 'Align text to the left.',
27629             cls: 'x-html-editor-tip'
27630         },
27631         justifycenter : {
27632             title: 'Center Text',
27633             text: 'Center text in the editor.',
27634             cls: 'x-html-editor-tip'
27635         },
27636         justifyright : {
27637             title: 'Align Text Right',
27638             text: 'Align text to the right.',
27639             cls: 'x-html-editor-tip'
27640         },
27641         insertunorderedlist : {
27642             title: 'Bullet List',
27643             text: 'Start a bulleted list.',
27644             cls: 'x-html-editor-tip'
27645         },
27646         insertorderedlist : {
27647             title: 'Numbered List',
27648             text: 'Start a numbered list.',
27649             cls: 'x-html-editor-tip'
27650         },
27651         createlink : {
27652             title: 'Hyperlink',
27653             text: 'Make the selected text a hyperlink.',
27654             cls: 'x-html-editor-tip'
27655         },
27656         sourceedit : {
27657             title: 'Source Edit',
27658             text: 'Switch to source editing mode.',
27659             cls: 'x-html-editor-tip'
27660         }
27661     },
27662     // private
27663     onDestroy : function(){
27664         if(this.rendered){
27665             
27666             this.tb.items.each(function(item){
27667                 if(item.menu){
27668                     item.menu.removeAll();
27669                     if(item.menu.el){
27670                         item.menu.el.destroy();
27671                     }
27672                 }
27673                 item.destroy();
27674             });
27675              
27676         }
27677     },
27678     onFirstFocus: function() {
27679         this.tb.items.each(function(item){
27680            item.enable();
27681         });
27682     }
27683 });
27684
27685
27686
27687
27688 // <script type="text/javascript">
27689 /*
27690  * Based on
27691  * Ext JS Library 1.1.1
27692  * Copyright(c) 2006-2007, Ext JS, LLC.
27693  *  
27694  
27695  */
27696
27697  
27698 /**
27699  * @class Roo.form.HtmlEditor.ToolbarContext
27700  * Context Toolbar
27701  * 
27702  * Usage:
27703  *
27704  new Roo.form.HtmlEditor({
27705     ....
27706     toolbars : [
27707         { xtype: 'ToolbarStandard', styles : {} }
27708         { xtype: 'ToolbarContext', disable : {} }
27709     ]
27710 })
27711
27712      
27713  * 
27714  * @config : {Object} disable List of elements to disable.. (not done yet.)
27715  * @config : {Object} styles  Map of styles available.
27716  * 
27717  */
27718
27719 Roo.form.HtmlEditor.ToolbarContext = function(config)
27720 {
27721     
27722     Roo.apply(this, config);
27723     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27724     // dont call parent... till later.
27725     this.styles = this.styles || {};
27726 }
27727
27728  
27729
27730 Roo.form.HtmlEditor.ToolbarContext.types = {
27731     'IMG' : {
27732         width : {
27733             title: "Width",
27734             width: 40
27735         },
27736         height:  {
27737             title: "Height",
27738             width: 40
27739         },
27740         align: {
27741             title: "Align",
27742             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27743             width : 80
27744             
27745         },
27746         border: {
27747             title: "Border",
27748             width: 40
27749         },
27750         alt: {
27751             title: "Alt",
27752             width: 120
27753         },
27754         src : {
27755             title: "Src",
27756             width: 220
27757         }
27758         
27759     },
27760     'A' : {
27761         name : {
27762             title: "Name",
27763             width: 50
27764         },
27765         target:  {
27766             title: "Target",
27767             width: 120
27768         },
27769         href:  {
27770             title: "Href",
27771             width: 220
27772         } // border?
27773         
27774     },
27775     'TABLE' : {
27776         rows : {
27777             title: "Rows",
27778             width: 20
27779         },
27780         cols : {
27781             title: "Cols",
27782             width: 20
27783         },
27784         width : {
27785             title: "Width",
27786             width: 40
27787         },
27788         height : {
27789             title: "Height",
27790             width: 40
27791         },
27792         border : {
27793             title: "Border",
27794             width: 20
27795         }
27796     },
27797     'TD' : {
27798         width : {
27799             title: "Width",
27800             width: 40
27801         },
27802         height : {
27803             title: "Height",
27804             width: 40
27805         },   
27806         align: {
27807             title: "Align",
27808             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27809             width: 80
27810         },
27811         valign: {
27812             title: "Valign",
27813             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27814             width: 80
27815         },
27816         colspan: {
27817             title: "Colspan",
27818             width: 20
27819             
27820         },
27821          'font-family'  : {
27822             title : "Font",
27823             style : 'fontFamily',
27824             displayField: 'display',
27825             optname : 'font-family',
27826             width: 140
27827         }
27828     },
27829     'INPUT' : {
27830         name : {
27831             title: "name",
27832             width: 120
27833         },
27834         value : {
27835             title: "Value",
27836             width: 120
27837         },
27838         width : {
27839             title: "Width",
27840             width: 40
27841         }
27842     },
27843     'LABEL' : {
27844         'for' : {
27845             title: "For",
27846             width: 120
27847         }
27848     },
27849     'TEXTAREA' : {
27850           name : {
27851             title: "name",
27852             width: 120
27853         },
27854         rows : {
27855             title: "Rows",
27856             width: 20
27857         },
27858         cols : {
27859             title: "Cols",
27860             width: 20
27861         }
27862     },
27863     'SELECT' : {
27864         name : {
27865             title: "name",
27866             width: 120
27867         },
27868         selectoptions : {
27869             title: "Options",
27870             width: 200
27871         }
27872     },
27873     
27874     // should we really allow this??
27875     // should this just be 
27876     'BODY' : {
27877         title : {
27878             title: "Title",
27879             width: 200,
27880             disabled : true
27881         }
27882     },
27883     'SPAN' : {
27884         'font-family'  : {
27885             title : "Font",
27886             style : 'fontFamily',
27887             displayField: 'display',
27888             optname : 'font-family',
27889             width: 140
27890         }
27891     },
27892     'DIV' : {
27893         'font-family'  : {
27894             title : "Font",
27895             style : 'fontFamily',
27896             displayField: 'display',
27897             optname : 'font-family',
27898             width: 140
27899         }
27900     },
27901      'P' : {
27902         'font-family'  : {
27903             title : "Font",
27904             style : 'fontFamily',
27905             displayField: 'display',
27906             optname : 'font-family',
27907             width: 140
27908         }
27909     },
27910     
27911     '*' : {
27912         // empty..
27913     }
27914
27915 };
27916
27917 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27918 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27919
27920 Roo.form.HtmlEditor.ToolbarContext.options = {
27921         'font-family'  : [ 
27922                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27923                 [ 'Courier New', 'Courier New'],
27924                 [ 'Tahoma', 'Tahoma'],
27925                 [ 'Times New Roman,serif', 'Times'],
27926                 [ 'Verdana','Verdana' ]
27927         ]
27928 };
27929
27930 // fixme - these need to be configurable..
27931  
27932
27933 Roo.form.HtmlEditor.ToolbarContext.types
27934
27935
27936 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27937     
27938     tb: false,
27939     
27940     rendered: false,
27941     
27942     editor : false,
27943     editorcore : false,
27944     /**
27945      * @cfg {Object} disable  List of toolbar elements to disable
27946          
27947      */
27948     disable : false,
27949     /**
27950      * @cfg {Object} styles List of styles 
27951      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27952      *
27953      * These must be defined in the page, so they get rendered correctly..
27954      * .headline { }
27955      * TD.underline { }
27956      * 
27957      */
27958     styles : false,
27959     
27960     options: false,
27961     
27962     toolbars : false,
27963     
27964     init : function(editor)
27965     {
27966         this.editor = editor;
27967         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27968         var editorcore = this.editorcore;
27969         
27970         var fid = editorcore.frameId;
27971         var etb = this;
27972         function btn(id, toggle, handler){
27973             var xid = fid + '-'+ id ;
27974             return {
27975                 id : xid,
27976                 cmd : id,
27977                 cls : 'x-btn-icon x-edit-'+id,
27978                 enableToggle:toggle !== false,
27979                 scope: editorcore, // was editor...
27980                 handler:handler||editorcore.relayBtnCmd,
27981                 clickEvent:'mousedown',
27982                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27983                 tabIndex:-1
27984             };
27985         }
27986         // create a new element.
27987         var wdiv = editor.wrap.createChild({
27988                 tag: 'div'
27989             }, editor.wrap.dom.firstChild.nextSibling, true);
27990         
27991         // can we do this more than once??
27992         
27993          // stop form submits
27994       
27995  
27996         // disable everything...
27997         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27998         this.toolbars = {};
27999            
28000         for (var i in  ty) {
28001           
28002             this.toolbars[i] = this.buildToolbar(ty[i],i);
28003         }
28004         this.tb = this.toolbars.BODY;
28005         this.tb.el.show();
28006         this.buildFooter();
28007         this.footer.show();
28008         editor.on('hide', function( ) { this.footer.hide() }, this);
28009         editor.on('show', function( ) { this.footer.show() }, this);
28010         
28011          
28012         this.rendered = true;
28013         
28014         // the all the btns;
28015         editor.on('editorevent', this.updateToolbar, this);
28016         // other toolbars need to implement this..
28017         //editor.on('editmodechange', this.updateToolbar, this);
28018     },
28019     
28020     
28021     
28022     /**
28023      * Protected method that will not generally be called directly. It triggers
28024      * a toolbar update by reading the markup state of the current selection in the editor.
28025      *
28026      * Note you can force an update by calling on('editorevent', scope, false)
28027      */
28028     updateToolbar: function(editor,ev,sel){
28029
28030         //Roo.log(ev);
28031         // capture mouse up - this is handy for selecting images..
28032         // perhaps should go somewhere else...
28033         if(!this.editorcore.activated){
28034              this.editor.onFirstFocus();
28035             return;
28036         }
28037         
28038         
28039         
28040         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28041         // selectNode - might want to handle IE?
28042         if (ev &&
28043             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28044             ev.target && ev.target.tagName == 'IMG') {
28045             // they have click on an image...
28046             // let's see if we can change the selection...
28047             sel = ev.target;
28048          
28049               var nodeRange = sel.ownerDocument.createRange();
28050             try {
28051                 nodeRange.selectNode(sel);
28052             } catch (e) {
28053                 nodeRange.selectNodeContents(sel);
28054             }
28055             //nodeRange.collapse(true);
28056             var s = this.editorcore.win.getSelection();
28057             s.removeAllRanges();
28058             s.addRange(nodeRange);
28059         }  
28060         
28061       
28062         var updateFooter = sel ? false : true;
28063         
28064         
28065         var ans = this.editorcore.getAllAncestors();
28066         
28067         // pick
28068         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28069         
28070         if (!sel) { 
28071             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28072             sel = sel ? sel : this.editorcore.doc.body;
28073             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28074             
28075         }
28076         // pick a menu that exists..
28077         var tn = sel.tagName.toUpperCase();
28078         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28079         
28080         tn = sel.tagName.toUpperCase();
28081         
28082         var lastSel = this.tb.selectedNode
28083         
28084         this.tb.selectedNode = sel;
28085         
28086         // if current menu does not match..
28087         
28088         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28089                 
28090             this.tb.el.hide();
28091             ///console.log("show: " + tn);
28092             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28093             this.tb.el.show();
28094             // update name
28095             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28096             
28097             
28098             // update attributes
28099             if (this.tb.fields) {
28100                 this.tb.fields.each(function(e) {
28101                     if (e.stylename) {
28102                         e.setValue(sel.style[e.stylename]);
28103                         return;
28104                     } 
28105                    e.setValue(sel.getAttribute(e.attrname));
28106                 });
28107             }
28108             
28109             var hasStyles = false;
28110             for(var i in this.styles) {
28111                 hasStyles = true;
28112                 break;
28113             }
28114             
28115             // update styles
28116             if (hasStyles) { 
28117                 var st = this.tb.fields.item(0);
28118                 
28119                 st.store.removeAll();
28120                
28121                 
28122                 var cn = sel.className.split(/\s+/);
28123                 
28124                 var avs = [];
28125                 if (this.styles['*']) {
28126                     
28127                     Roo.each(this.styles['*'], function(v) {
28128                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28129                     });
28130                 }
28131                 if (this.styles[tn]) { 
28132                     Roo.each(this.styles[tn], function(v) {
28133                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28134                     });
28135                 }
28136                 
28137                 st.store.loadData(avs);
28138                 st.collapse();
28139                 st.setValue(cn);
28140             }
28141             // flag our selected Node.
28142             this.tb.selectedNode = sel;
28143            
28144            
28145             Roo.menu.MenuMgr.hideAll();
28146
28147         }
28148         
28149         if (!updateFooter) {
28150             //this.footDisp.dom.innerHTML = ''; 
28151             return;
28152         }
28153         // update the footer
28154         //
28155         var html = '';
28156         
28157         this.footerEls = ans.reverse();
28158         Roo.each(this.footerEls, function(a,i) {
28159             if (!a) { return; }
28160             html += html.length ? ' &gt; '  :  '';
28161             
28162             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28163             
28164         });
28165        
28166         // 
28167         var sz = this.footDisp.up('td').getSize();
28168         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28169         this.footDisp.dom.style.marginLeft = '5px';
28170         
28171         this.footDisp.dom.style.overflow = 'hidden';
28172         
28173         this.footDisp.dom.innerHTML = html;
28174             
28175         //this.editorsyncValue();
28176     },
28177      
28178     
28179    
28180        
28181     // private
28182     onDestroy : function(){
28183         if(this.rendered){
28184             
28185             this.tb.items.each(function(item){
28186                 if(item.menu){
28187                     item.menu.removeAll();
28188                     if(item.menu.el){
28189                         item.menu.el.destroy();
28190                     }
28191                 }
28192                 item.destroy();
28193             });
28194              
28195         }
28196     },
28197     onFirstFocus: function() {
28198         // need to do this for all the toolbars..
28199         this.tb.items.each(function(item){
28200            item.enable();
28201         });
28202     },
28203     buildToolbar: function(tlist, nm)
28204     {
28205         var editor = this.editor;
28206         var editorcore = this.editorcore;
28207          // create a new element.
28208         var wdiv = editor.wrap.createChild({
28209                 tag: 'div'
28210             }, editor.wrap.dom.firstChild.nextSibling, true);
28211         
28212        
28213         var tb = new Roo.Toolbar(wdiv);
28214         // add the name..
28215         
28216         tb.add(nm+ ":&nbsp;");
28217         
28218         var styles = [];
28219         for(var i in this.styles) {
28220             styles.push(i);
28221         }
28222         
28223         // styles...
28224         if (styles && styles.length) {
28225             
28226             // this needs a multi-select checkbox...
28227             tb.addField( new Roo.form.ComboBox({
28228                 store: new Roo.data.SimpleStore({
28229                     id : 'val',
28230                     fields: ['val', 'selected'],
28231                     data : [] 
28232                 }),
28233                 name : '-roo-edit-className',
28234                 attrname : 'className',
28235                 displayField: 'val',
28236                 typeAhead: false,
28237                 mode: 'local',
28238                 editable : false,
28239                 triggerAction: 'all',
28240                 emptyText:'Select Style',
28241                 selectOnFocus:true,
28242                 width: 130,
28243                 listeners : {
28244                     'select': function(c, r, i) {
28245                         // initial support only for on class per el..
28246                         tb.selectedNode.className =  r ? r.get('val') : '';
28247                         editorcore.syncValue();
28248                     }
28249                 }
28250     
28251             }));
28252         }
28253         
28254         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28255         var tbops = tbc.options;
28256         
28257         for (var i in tlist) {
28258             
28259             var item = tlist[i];
28260             tb.add(item.title + ":&nbsp;");
28261             
28262             
28263             //optname == used so you can configure the options available..
28264             var opts = item.opts ? item.opts : false;
28265             if (item.optname) {
28266                 opts = tbops[item.optname];
28267            
28268             }
28269             
28270             if (opts) {
28271                 // opts == pulldown..
28272                 tb.addField( new Roo.form.ComboBox({
28273                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28274                         id : 'val',
28275                         fields: ['val', 'display'],
28276                         data : opts  
28277                     }),
28278                     name : '-roo-edit-' + i,
28279                     attrname : i,
28280                     stylename : item.style ? item.style : false,
28281                     displayField: item.displayField ? item.displayField : 'val',
28282                     valueField :  'val',
28283                     typeAhead: false,
28284                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28285                     editable : false,
28286                     triggerAction: 'all',
28287                     emptyText:'Select',
28288                     selectOnFocus:true,
28289                     width: item.width ? item.width  : 130,
28290                     listeners : {
28291                         'select': function(c, r, i) {
28292                             if (c.stylename) {
28293                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28294                                 return;
28295                             }
28296                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28297                         }
28298                     }
28299
28300                 }));
28301                 continue;
28302                     
28303                  
28304                 
28305                 tb.addField( new Roo.form.TextField({
28306                     name: i,
28307                     width: 100,
28308                     //allowBlank:false,
28309                     value: ''
28310                 }));
28311                 continue;
28312             }
28313             tb.addField( new Roo.form.TextField({
28314                 name: '-roo-edit-' + i,
28315                 attrname : i,
28316                 
28317                 width: item.width,
28318                 //allowBlank:true,
28319                 value: '',
28320                 listeners: {
28321                     'change' : function(f, nv, ov) {
28322                         tb.selectedNode.setAttribute(f.attrname, nv);
28323                     }
28324                 }
28325             }));
28326              
28327         }
28328         
28329         var _this = this;
28330         
28331         if(nm == 'BODY'){
28332             tb.addSeparator();
28333         
28334             tb.addButton( {
28335                 text: 'Stylesheets',
28336
28337                 listeners : {
28338                     click : function ()
28339                     {
28340                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28341                     }
28342                 }
28343             });
28344         }
28345         
28346         tb.addFill();
28347         tb.addButton( {
28348             text: 'Remove Tag',
28349     
28350             listeners : {
28351                 click : function ()
28352                 {
28353                     // remove
28354                     // undo does not work.
28355                      
28356                     var sn = tb.selectedNode;
28357                     
28358                     var pn = sn.parentNode;
28359                     
28360                     var stn =  sn.childNodes[0];
28361                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28362                     while (sn.childNodes.length) {
28363                         var node = sn.childNodes[0];
28364                         sn.removeChild(node);
28365                         //Roo.log(node);
28366                         pn.insertBefore(node, sn);
28367                         
28368                     }
28369                     pn.removeChild(sn);
28370                     var range = editorcore.createRange();
28371         
28372                     range.setStart(stn,0);
28373                     range.setEnd(en,0); //????
28374                     //range.selectNode(sel);
28375                     
28376                     
28377                     var selection = editorcore.getSelection();
28378                     selection.removeAllRanges();
28379                     selection.addRange(range);
28380                     
28381                     
28382                     
28383                     //_this.updateToolbar(null, null, pn);
28384                     _this.updateToolbar(null, null, null);
28385                     _this.footDisp.dom.innerHTML = ''; 
28386                 }
28387             }
28388             
28389                     
28390                 
28391             
28392         });
28393         
28394         
28395         tb.el.on('click', function(e){
28396             e.preventDefault(); // what does this do?
28397         });
28398         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28399         tb.el.hide();
28400         tb.name = nm;
28401         // dont need to disable them... as they will get hidden
28402         return tb;
28403          
28404         
28405     },
28406     buildFooter : function()
28407     {
28408         
28409         var fel = this.editor.wrap.createChild();
28410         this.footer = new Roo.Toolbar(fel);
28411         // toolbar has scrolly on left / right?
28412         var footDisp= new Roo.Toolbar.Fill();
28413         var _t = this;
28414         this.footer.add(
28415             {
28416                 text : '&lt;',
28417                 xtype: 'Button',
28418                 handler : function() {
28419                     _t.footDisp.scrollTo('left',0,true)
28420                 }
28421             }
28422         );
28423         this.footer.add( footDisp );
28424         this.footer.add( 
28425             {
28426                 text : '&gt;',
28427                 xtype: 'Button',
28428                 handler : function() {
28429                     // no animation..
28430                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28431                 }
28432             }
28433         );
28434         var fel = Roo.get(footDisp.el);
28435         fel.addClass('x-editor-context');
28436         this.footDispWrap = fel; 
28437         this.footDispWrap.overflow  = 'hidden';
28438         
28439         this.footDisp = fel.createChild();
28440         this.footDispWrap.on('click', this.onContextClick, this)
28441         
28442         
28443     },
28444     onContextClick : function (ev,dom)
28445     {
28446         ev.preventDefault();
28447         var  cn = dom.className;
28448         //Roo.log(cn);
28449         if (!cn.match(/x-ed-loc-/)) {
28450             return;
28451         }
28452         var n = cn.split('-').pop();
28453         var ans = this.footerEls;
28454         var sel = ans[n];
28455         
28456          // pick
28457         var range = this.editorcore.createRange();
28458         
28459         range.selectNodeContents(sel);
28460         //range.selectNode(sel);
28461         
28462         
28463         var selection = this.editorcore.getSelection();
28464         selection.removeAllRanges();
28465         selection.addRange(range);
28466         
28467         
28468         
28469         this.updateToolbar(null, null, sel);
28470         
28471         
28472     }
28473     
28474     
28475     
28476     
28477     
28478 });
28479
28480
28481
28482
28483
28484 /*
28485  * Based on:
28486  * Ext JS Library 1.1.1
28487  * Copyright(c) 2006-2007, Ext JS, LLC.
28488  *
28489  * Originally Released Under LGPL - original licence link has changed is not relivant.
28490  *
28491  * Fork - LGPL
28492  * <script type="text/javascript">
28493  */
28494  
28495 /**
28496  * @class Roo.form.BasicForm
28497  * @extends Roo.util.Observable
28498  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28499  * @constructor
28500  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28501  * @param {Object} config Configuration options
28502  */
28503 Roo.form.BasicForm = function(el, config){
28504     this.allItems = [];
28505     this.childForms = [];
28506     Roo.apply(this, config);
28507     /*
28508      * The Roo.form.Field items in this form.
28509      * @type MixedCollection
28510      */
28511      
28512      
28513     this.items = new Roo.util.MixedCollection(false, function(o){
28514         return o.id || (o.id = Roo.id());
28515     });
28516     this.addEvents({
28517         /**
28518          * @event beforeaction
28519          * Fires before any action is performed. Return false to cancel the action.
28520          * @param {Form} this
28521          * @param {Action} action The action to be performed
28522          */
28523         beforeaction: true,
28524         /**
28525          * @event actionfailed
28526          * Fires when an action fails.
28527          * @param {Form} this
28528          * @param {Action} action The action that failed
28529          */
28530         actionfailed : true,
28531         /**
28532          * @event actioncomplete
28533          * Fires when an action is completed.
28534          * @param {Form} this
28535          * @param {Action} action The action that completed
28536          */
28537         actioncomplete : true
28538     });
28539     if(el){
28540         this.initEl(el);
28541     }
28542     Roo.form.BasicForm.superclass.constructor.call(this);
28543 };
28544
28545 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28546     /**
28547      * @cfg {String} method
28548      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28549      */
28550     /**
28551      * @cfg {DataReader} reader
28552      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28553      * This is optional as there is built-in support for processing JSON.
28554      */
28555     /**
28556      * @cfg {DataReader} errorReader
28557      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28558      * This is completely optional as there is built-in support for processing JSON.
28559      */
28560     /**
28561      * @cfg {String} url
28562      * The URL to use for form actions if one isn't supplied in the action options.
28563      */
28564     /**
28565      * @cfg {Boolean} fileUpload
28566      * Set to true if this form is a file upload.
28567      */
28568      
28569     /**
28570      * @cfg {Object} baseParams
28571      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28572      */
28573      /**
28574      
28575     /**
28576      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28577      */
28578     timeout: 30,
28579
28580     // private
28581     activeAction : null,
28582
28583     /**
28584      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28585      * or setValues() data instead of when the form was first created.
28586      */
28587     trackResetOnLoad : false,
28588     
28589     
28590     /**
28591      * childForms - used for multi-tab forms
28592      * @type {Array}
28593      */
28594     childForms : false,
28595     
28596     /**
28597      * allItems - full list of fields.
28598      * @type {Array}
28599      */
28600     allItems : false,
28601     
28602     /**
28603      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28604      * element by passing it or its id or mask the form itself by passing in true.
28605      * @type Mixed
28606      */
28607     waitMsgTarget : false,
28608
28609     // private
28610     initEl : function(el){
28611         this.el = Roo.get(el);
28612         this.id = this.el.id || Roo.id();
28613         this.el.on('submit', this.onSubmit, this);
28614         this.el.addClass('x-form');
28615     },
28616
28617     // private
28618     onSubmit : function(e){
28619         e.stopEvent();
28620     },
28621
28622     /**
28623      * Returns true if client-side validation on the form is successful.
28624      * @return Boolean
28625      */
28626     isValid : function(){
28627         var valid = true;
28628         this.items.each(function(f){
28629            if(!f.validate()){
28630                valid = false;
28631            }
28632         });
28633         return valid;
28634     },
28635
28636     /**
28637      * Returns true if any fields in this form have changed since their original load.
28638      * @return Boolean
28639      */
28640     isDirty : function(){
28641         var dirty = false;
28642         this.items.each(function(f){
28643            if(f.isDirty()){
28644                dirty = true;
28645                return false;
28646            }
28647         });
28648         return dirty;
28649     },
28650
28651     /**
28652      * Performs a predefined action (submit or load) or custom actions you define on this form.
28653      * @param {String} actionName The name of the action type
28654      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28655      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28656      * accept other config options):
28657      * <pre>
28658 Property          Type             Description
28659 ----------------  ---------------  ----------------------------------------------------------------------------------
28660 url               String           The url for the action (defaults to the form's url)
28661 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28662 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28663 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28664                                    validate the form on the client (defaults to false)
28665      * </pre>
28666      * @return {BasicForm} this
28667      */
28668     doAction : function(action, options){
28669         if(typeof action == 'string'){
28670             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28671         }
28672         if(this.fireEvent('beforeaction', this, action) !== false){
28673             this.beforeAction(action);
28674             action.run.defer(100, action);
28675         }
28676         return this;
28677     },
28678
28679     /**
28680      * Shortcut to do a submit action.
28681      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28682      * @return {BasicForm} this
28683      */
28684     submit : function(options){
28685         this.doAction('submit', options);
28686         return this;
28687     },
28688
28689     /**
28690      * Shortcut to do a load action.
28691      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28692      * @return {BasicForm} this
28693      */
28694     load : function(options){
28695         this.doAction('load', options);
28696         return this;
28697     },
28698
28699     /**
28700      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28701      * @param {Record} record The record to edit
28702      * @return {BasicForm} this
28703      */
28704     updateRecord : function(record){
28705         record.beginEdit();
28706         var fs = record.fields;
28707         fs.each(function(f){
28708             var field = this.findField(f.name);
28709             if(field){
28710                 record.set(f.name, field.getValue());
28711             }
28712         }, this);
28713         record.endEdit();
28714         return this;
28715     },
28716
28717     /**
28718      * Loads an Roo.data.Record into this form.
28719      * @param {Record} record The record to load
28720      * @return {BasicForm} this
28721      */
28722     loadRecord : function(record){
28723         this.setValues(record.data);
28724         return this;
28725     },
28726
28727     // private
28728     beforeAction : function(action){
28729         var o = action.options;
28730         
28731        
28732         if(this.waitMsgTarget === true){
28733             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28734         }else if(this.waitMsgTarget){
28735             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28736             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28737         }else {
28738             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28739         }
28740          
28741     },
28742
28743     // private
28744     afterAction : function(action, success){
28745         this.activeAction = null;
28746         var o = action.options;
28747         
28748         if(this.waitMsgTarget === true){
28749             this.el.unmask();
28750         }else if(this.waitMsgTarget){
28751             this.waitMsgTarget.unmask();
28752         }else{
28753             Roo.MessageBox.updateProgress(1);
28754             Roo.MessageBox.hide();
28755         }
28756          
28757         if(success){
28758             if(o.reset){
28759                 this.reset();
28760             }
28761             Roo.callback(o.success, o.scope, [this, action]);
28762             this.fireEvent('actioncomplete', this, action);
28763             
28764         }else{
28765             
28766             // failure condition..
28767             // we have a scenario where updates need confirming.
28768             // eg. if a locking scenario exists..
28769             // we look for { errors : { needs_confirm : true }} in the response.
28770             if (
28771                 (typeof(action.result) != 'undefined')  &&
28772                 (typeof(action.result.errors) != 'undefined')  &&
28773                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28774            ){
28775                 var _t = this;
28776                 Roo.MessageBox.confirm(
28777                     "Change requires confirmation",
28778                     action.result.errorMsg,
28779                     function(r) {
28780                         if (r != 'yes') {
28781                             return;
28782                         }
28783                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28784                     }
28785                     
28786                 );
28787                 
28788                 
28789                 
28790                 return;
28791             }
28792             
28793             Roo.callback(o.failure, o.scope, [this, action]);
28794             // show an error message if no failed handler is set..
28795             if (!this.hasListener('actionfailed')) {
28796                 Roo.MessageBox.alert("Error",
28797                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28798                         action.result.errorMsg :
28799                         "Saving Failed, please check your entries or try again"
28800                 );
28801             }
28802             
28803             this.fireEvent('actionfailed', this, action);
28804         }
28805         
28806     },
28807
28808     /**
28809      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28810      * @param {String} id The value to search for
28811      * @return Field
28812      */
28813     findField : function(id){
28814         var field = this.items.get(id);
28815         if(!field){
28816             this.items.each(function(f){
28817                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28818                     field = f;
28819                     return false;
28820                 }
28821             });
28822         }
28823         return field || null;
28824     },
28825
28826     /**
28827      * Add a secondary form to this one, 
28828      * Used to provide tabbed forms. One form is primary, with hidden values 
28829      * which mirror the elements from the other forms.
28830      * 
28831      * @param {Roo.form.Form} form to add.
28832      * 
28833      */
28834     addForm : function(form)
28835     {
28836        
28837         if (this.childForms.indexOf(form) > -1) {
28838             // already added..
28839             return;
28840         }
28841         this.childForms.push(form);
28842         var n = '';
28843         Roo.each(form.allItems, function (fe) {
28844             
28845             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28846             if (this.findField(n)) { // already added..
28847                 return;
28848             }
28849             var add = new Roo.form.Hidden({
28850                 name : n
28851             });
28852             add.render(this.el);
28853             
28854             this.add( add );
28855         }, this);
28856         
28857     },
28858     /**
28859      * Mark fields in this form invalid in bulk.
28860      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28861      * @return {BasicForm} this
28862      */
28863     markInvalid : function(errors){
28864         if(errors instanceof Array){
28865             for(var i = 0, len = errors.length; i < len; i++){
28866                 var fieldError = errors[i];
28867                 var f = this.findField(fieldError.id);
28868                 if(f){
28869                     f.markInvalid(fieldError.msg);
28870                 }
28871             }
28872         }else{
28873             var field, id;
28874             for(id in errors){
28875                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28876                     field.markInvalid(errors[id]);
28877                 }
28878             }
28879         }
28880         Roo.each(this.childForms || [], function (f) {
28881             f.markInvalid(errors);
28882         });
28883         
28884         return this;
28885     },
28886
28887     /**
28888      * Set values for fields in this form in bulk.
28889      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28890      * @return {BasicForm} this
28891      */
28892     setValues : function(values){
28893         if(values instanceof Array){ // array of objects
28894             for(var i = 0, len = values.length; i < len; i++){
28895                 var v = values[i];
28896                 var f = this.findField(v.id);
28897                 if(f){
28898                     f.setValue(v.value);
28899                     if(this.trackResetOnLoad){
28900                         f.originalValue = f.getValue();
28901                     }
28902                 }
28903             }
28904         }else{ // object hash
28905             var field, id;
28906             for(id in values){
28907                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28908                     
28909                     if (field.setFromData && 
28910                         field.valueField && 
28911                         field.displayField &&
28912                         // combos' with local stores can 
28913                         // be queried via setValue()
28914                         // to set their value..
28915                         (field.store && !field.store.isLocal)
28916                         ) {
28917                         // it's a combo
28918                         var sd = { };
28919                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28920                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28921                         field.setFromData(sd);
28922                         
28923                     } else {
28924                         field.setValue(values[id]);
28925                     }
28926                     
28927                     
28928                     if(this.trackResetOnLoad){
28929                         field.originalValue = field.getValue();
28930                     }
28931                 }
28932             }
28933         }
28934          
28935         Roo.each(this.childForms || [], function (f) {
28936             f.setValues(values);
28937         });
28938                 
28939         return this;
28940     },
28941
28942     /**
28943      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28944      * they are returned as an array.
28945      * @param {Boolean} asString
28946      * @return {Object}
28947      */
28948     getValues : function(asString){
28949         if (this.childForms) {
28950             // copy values from the child forms
28951             Roo.each(this.childForms, function (f) {
28952                 this.setValues(f.getValues());
28953             }, this);
28954         }
28955         
28956         
28957         
28958         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28959         if(asString === true){
28960             return fs;
28961         }
28962         return Roo.urlDecode(fs);
28963     },
28964     
28965     /**
28966      * Returns the fields in this form as an object with key/value pairs. 
28967      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28968      * @return {Object}
28969      */
28970     getFieldValues : function(with_hidden)
28971     {
28972         if (this.childForms) {
28973             // copy values from the child forms
28974             // should this call getFieldValues - probably not as we do not currently copy
28975             // hidden fields when we generate..
28976             Roo.each(this.childForms, function (f) {
28977                 this.setValues(f.getValues());
28978             }, this);
28979         }
28980         
28981         var ret = {};
28982         this.items.each(function(f){
28983             if (!f.getName()) {
28984                 return;
28985             }
28986             var v = f.getValue();
28987             if (f.inputType =='radio') {
28988                 if (typeof(ret[f.getName()]) == 'undefined') {
28989                     ret[f.getName()] = ''; // empty..
28990                 }
28991                 
28992                 if (!f.el.dom.checked) {
28993                     return;
28994                     
28995                 }
28996                 v = f.el.dom.value;
28997                 
28998             }
28999             
29000             // not sure if this supported any more..
29001             if ((typeof(v) == 'object') && f.getRawValue) {
29002                 v = f.getRawValue() ; // dates..
29003             }
29004             // combo boxes where name != hiddenName...
29005             if (f.name != f.getName()) {
29006                 ret[f.name] = f.getRawValue();
29007             }
29008             ret[f.getName()] = v;
29009         });
29010         
29011         return ret;
29012     },
29013
29014     /**
29015      * Clears all invalid messages in this form.
29016      * @return {BasicForm} this
29017      */
29018     clearInvalid : function(){
29019         this.items.each(function(f){
29020            f.clearInvalid();
29021         });
29022         
29023         Roo.each(this.childForms || [], function (f) {
29024             f.clearInvalid();
29025         });
29026         
29027         
29028         return this;
29029     },
29030
29031     /**
29032      * Resets this form.
29033      * @return {BasicForm} this
29034      */
29035     reset : function(){
29036         this.items.each(function(f){
29037             f.reset();
29038         });
29039         
29040         Roo.each(this.childForms || [], function (f) {
29041             f.reset();
29042         });
29043        
29044         
29045         return this;
29046     },
29047
29048     /**
29049      * Add Roo.form components to this form.
29050      * @param {Field} field1
29051      * @param {Field} field2 (optional)
29052      * @param {Field} etc (optional)
29053      * @return {BasicForm} this
29054      */
29055     add : function(){
29056         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29057         return this;
29058     },
29059
29060
29061     /**
29062      * Removes a field from the items collection (does NOT remove its markup).
29063      * @param {Field} field
29064      * @return {BasicForm} this
29065      */
29066     remove : function(field){
29067         this.items.remove(field);
29068         return this;
29069     },
29070
29071     /**
29072      * Looks at the fields in this form, checks them for an id attribute,
29073      * and calls applyTo on the existing dom element with that id.
29074      * @return {BasicForm} this
29075      */
29076     render : function(){
29077         this.items.each(function(f){
29078             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29079                 f.applyTo(f.id);
29080             }
29081         });
29082         return this;
29083     },
29084
29085     /**
29086      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29087      * @param {Object} values
29088      * @return {BasicForm} this
29089      */
29090     applyToFields : function(o){
29091         this.items.each(function(f){
29092            Roo.apply(f, o);
29093         });
29094         return this;
29095     },
29096
29097     /**
29098      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29099      * @param {Object} values
29100      * @return {BasicForm} this
29101      */
29102     applyIfToFields : function(o){
29103         this.items.each(function(f){
29104            Roo.applyIf(f, o);
29105         });
29106         return this;
29107     }
29108 });
29109
29110 // back compat
29111 Roo.BasicForm = Roo.form.BasicForm;/*
29112  * Based on:
29113  * Ext JS Library 1.1.1
29114  * Copyright(c) 2006-2007, Ext JS, LLC.
29115  *
29116  * Originally Released Under LGPL - original licence link has changed is not relivant.
29117  *
29118  * Fork - LGPL
29119  * <script type="text/javascript">
29120  */
29121
29122 /**
29123  * @class Roo.form.Form
29124  * @extends Roo.form.BasicForm
29125  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29126  * @constructor
29127  * @param {Object} config Configuration options
29128  */
29129 Roo.form.Form = function(config){
29130     var xitems =  [];
29131     if (config.items) {
29132         xitems = config.items;
29133         delete config.items;
29134     }
29135    
29136     
29137     Roo.form.Form.superclass.constructor.call(this, null, config);
29138     this.url = this.url || this.action;
29139     if(!this.root){
29140         this.root = new Roo.form.Layout(Roo.applyIf({
29141             id: Roo.id()
29142         }, config));
29143     }
29144     this.active = this.root;
29145     /**
29146      * Array of all the buttons that have been added to this form via {@link addButton}
29147      * @type Array
29148      */
29149     this.buttons = [];
29150     this.allItems = [];
29151     this.addEvents({
29152         /**
29153          * @event clientvalidation
29154          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29155          * @param {Form} this
29156          * @param {Boolean} valid true if the form has passed client-side validation
29157          */
29158         clientvalidation: true,
29159         /**
29160          * @event rendered
29161          * Fires when the form is rendered
29162          * @param {Roo.form.Form} form
29163          */
29164         rendered : true
29165     });
29166     
29167     if (this.progressUrl) {
29168             // push a hidden field onto the list of fields..
29169             this.addxtype( {
29170                     xns: Roo.form, 
29171                     xtype : 'Hidden', 
29172                     name : 'UPLOAD_IDENTIFIER' 
29173             });
29174         }
29175         
29176     
29177     Roo.each(xitems, this.addxtype, this);
29178     
29179     
29180     
29181 };
29182
29183 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29184     /**
29185      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29186      */
29187     /**
29188      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29189      */
29190     /**
29191      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29192      */
29193     buttonAlign:'center',
29194
29195     /**
29196      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29197      */
29198     minButtonWidth:75,
29199
29200     /**
29201      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29202      * This property cascades to child containers if not set.
29203      */
29204     labelAlign:'left',
29205
29206     /**
29207      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29208      * fires a looping event with that state. This is required to bind buttons to the valid
29209      * state using the config value formBind:true on the button.
29210      */
29211     monitorValid : false,
29212
29213     /**
29214      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29215      */
29216     monitorPoll : 200,
29217     
29218     /**
29219      * @cfg {String} progressUrl - Url to return progress data 
29220      */
29221     
29222     progressUrl : false,
29223   
29224     /**
29225      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29226      * fields are added and the column is closed. If no fields are passed the column remains open
29227      * until end() is called.
29228      * @param {Object} config The config to pass to the column
29229      * @param {Field} field1 (optional)
29230      * @param {Field} field2 (optional)
29231      * @param {Field} etc (optional)
29232      * @return Column The column container object
29233      */
29234     column : function(c){
29235         var col = new Roo.form.Column(c);
29236         this.start(col);
29237         if(arguments.length > 1){ // duplicate code required because of Opera
29238             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29239             this.end();
29240         }
29241         return col;
29242     },
29243
29244     /**
29245      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29246      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29247      * until end() is called.
29248      * @param {Object} config The config to pass to the fieldset
29249      * @param {Field} field1 (optional)
29250      * @param {Field} field2 (optional)
29251      * @param {Field} etc (optional)
29252      * @return FieldSet The fieldset container object
29253      */
29254     fieldset : function(c){
29255         var fs = new Roo.form.FieldSet(c);
29256         this.start(fs);
29257         if(arguments.length > 1){ // duplicate code required because of Opera
29258             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29259             this.end();
29260         }
29261         return fs;
29262     },
29263
29264     /**
29265      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29266      * fields are added and the container is closed. If no fields are passed the container remains open
29267      * until end() is called.
29268      * @param {Object} config The config to pass to the Layout
29269      * @param {Field} field1 (optional)
29270      * @param {Field} field2 (optional)
29271      * @param {Field} etc (optional)
29272      * @return Layout The container object
29273      */
29274     container : function(c){
29275         var l = new Roo.form.Layout(c);
29276         this.start(l);
29277         if(arguments.length > 1){ // duplicate code required because of Opera
29278             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29279             this.end();
29280         }
29281         return l;
29282     },
29283
29284     /**
29285      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29286      * @param {Object} container A Roo.form.Layout or subclass of Layout
29287      * @return {Form} this
29288      */
29289     start : function(c){
29290         // cascade label info
29291         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29292         this.active.stack.push(c);
29293         c.ownerCt = this.active;
29294         this.active = c;
29295         return this;
29296     },
29297
29298     /**
29299      * Closes the current open container
29300      * @return {Form} this
29301      */
29302     end : function(){
29303         if(this.active == this.root){
29304             return this;
29305         }
29306         this.active = this.active.ownerCt;
29307         return this;
29308     },
29309
29310     /**
29311      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29312      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29313      * as the label of the field.
29314      * @param {Field} field1
29315      * @param {Field} field2 (optional)
29316      * @param {Field} etc. (optional)
29317      * @return {Form} this
29318      */
29319     add : function(){
29320         this.active.stack.push.apply(this.active.stack, arguments);
29321         this.allItems.push.apply(this.allItems,arguments);
29322         var r = [];
29323         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29324             if(a[i].isFormField){
29325                 r.push(a[i]);
29326             }
29327         }
29328         if(r.length > 0){
29329             Roo.form.Form.superclass.add.apply(this, r);
29330         }
29331         return this;
29332     },
29333     
29334
29335     
29336     
29337     
29338      /**
29339      * Find any element that has been added to a form, using it's ID or name
29340      * This can include framesets, columns etc. along with regular fields..
29341      * @param {String} id - id or name to find.
29342      
29343      * @return {Element} e - or false if nothing found.
29344      */
29345     findbyId : function(id)
29346     {
29347         var ret = false;
29348         if (!id) {
29349             return ret;
29350         }
29351         Roo.each(this.allItems, function(f){
29352             if (f.id == id || f.name == id ){
29353                 ret = f;
29354                 return false;
29355             }
29356         });
29357         return ret;
29358     },
29359
29360     
29361     
29362     /**
29363      * Render this form into the passed container. This should only be called once!
29364      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29365      * @return {Form} this
29366      */
29367     render : function(ct)
29368     {
29369         
29370         
29371         
29372         ct = Roo.get(ct);
29373         var o = this.autoCreate || {
29374             tag: 'form',
29375             method : this.method || 'POST',
29376             id : this.id || Roo.id()
29377         };
29378         this.initEl(ct.createChild(o));
29379
29380         this.root.render(this.el);
29381         
29382        
29383              
29384         this.items.each(function(f){
29385             f.render('x-form-el-'+f.id);
29386         });
29387
29388         if(this.buttons.length > 0){
29389             // tables are required to maintain order and for correct IE layout
29390             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29391                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29392                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29393             }}, null, true);
29394             var tr = tb.getElementsByTagName('tr')[0];
29395             for(var i = 0, len = this.buttons.length; i < len; i++) {
29396                 var b = this.buttons[i];
29397                 var td = document.createElement('td');
29398                 td.className = 'x-form-btn-td';
29399                 b.render(tr.appendChild(td));
29400             }
29401         }
29402         if(this.monitorValid){ // initialize after render
29403             this.startMonitoring();
29404         }
29405         this.fireEvent('rendered', this);
29406         return this;
29407     },
29408
29409     /**
29410      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29411      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29412      * object or a valid Roo.DomHelper element config
29413      * @param {Function} handler The function called when the button is clicked
29414      * @param {Object} scope (optional) The scope of the handler function
29415      * @return {Roo.Button}
29416      */
29417     addButton : function(config, handler, scope){
29418         var bc = {
29419             handler: handler,
29420             scope: scope,
29421             minWidth: this.minButtonWidth,
29422             hideParent:true
29423         };
29424         if(typeof config == "string"){
29425             bc.text = config;
29426         }else{
29427             Roo.apply(bc, config);
29428         }
29429         var btn = new Roo.Button(null, bc);
29430         this.buttons.push(btn);
29431         return btn;
29432     },
29433
29434      /**
29435      * Adds a series of form elements (using the xtype property as the factory method.
29436      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29437      * @param {Object} config 
29438      */
29439     
29440     addxtype : function()
29441     {
29442         var ar = Array.prototype.slice.call(arguments, 0);
29443         var ret = false;
29444         for(var i = 0; i < ar.length; i++) {
29445             if (!ar[i]) {
29446                 continue; // skip -- if this happends something invalid got sent, we 
29447                 // should ignore it, as basically that interface element will not show up
29448                 // and that should be pretty obvious!!
29449             }
29450             
29451             if (Roo.form[ar[i].xtype]) {
29452                 ar[i].form = this;
29453                 var fe = Roo.factory(ar[i], Roo.form);
29454                 if (!ret) {
29455                     ret = fe;
29456                 }
29457                 fe.form = this;
29458                 if (fe.store) {
29459                     fe.store.form = this;
29460                 }
29461                 if (fe.isLayout) {  
29462                          
29463                     this.start(fe);
29464                     this.allItems.push(fe);
29465                     if (fe.items && fe.addxtype) {
29466                         fe.addxtype.apply(fe, fe.items);
29467                         delete fe.items;
29468                     }
29469                      this.end();
29470                     continue;
29471                 }
29472                 
29473                 
29474                  
29475                 this.add(fe);
29476               //  console.log('adding ' + ar[i].xtype);
29477             }
29478             if (ar[i].xtype == 'Button') {  
29479                 //console.log('adding button');
29480                 //console.log(ar[i]);
29481                 this.addButton(ar[i]);
29482                 this.allItems.push(fe);
29483                 continue;
29484             }
29485             
29486             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29487                 alert('end is not supported on xtype any more, use items');
29488             //    this.end();
29489             //    //console.log('adding end');
29490             }
29491             
29492         }
29493         return ret;
29494     },
29495     
29496     /**
29497      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29498      * option "monitorValid"
29499      */
29500     startMonitoring : function(){
29501         if(!this.bound){
29502             this.bound = true;
29503             Roo.TaskMgr.start({
29504                 run : this.bindHandler,
29505                 interval : this.monitorPoll || 200,
29506                 scope: this
29507             });
29508         }
29509     },
29510
29511     /**
29512      * Stops monitoring of the valid state of this form
29513      */
29514     stopMonitoring : function(){
29515         this.bound = false;
29516     },
29517
29518     // private
29519     bindHandler : function(){
29520         if(!this.bound){
29521             return false; // stops binding
29522         }
29523         var valid = true;
29524         this.items.each(function(f){
29525             if(!f.isValid(true)){
29526                 valid = false;
29527                 return false;
29528             }
29529         });
29530         for(var i = 0, len = this.buttons.length; i < len; i++){
29531             var btn = this.buttons[i];
29532             if(btn.formBind === true && btn.disabled === valid){
29533                 btn.setDisabled(!valid);
29534             }
29535         }
29536         this.fireEvent('clientvalidation', this, valid);
29537     }
29538     
29539     
29540     
29541     
29542     
29543     
29544     
29545     
29546 });
29547
29548
29549 // back compat
29550 Roo.Form = Roo.form.Form;
29551 /*
29552  * Based on:
29553  * Ext JS Library 1.1.1
29554  * Copyright(c) 2006-2007, Ext JS, LLC.
29555  *
29556  * Originally Released Under LGPL - original licence link has changed is not relivant.
29557  *
29558  * Fork - LGPL
29559  * <script type="text/javascript">
29560  */
29561
29562 // as we use this in bootstrap.
29563 Roo.namespace('Roo.form');
29564  /**
29565  * @class Roo.form.Action
29566  * Internal Class used to handle form actions
29567  * @constructor
29568  * @param {Roo.form.BasicForm} el The form element or its id
29569  * @param {Object} config Configuration options
29570  */
29571
29572  
29573  
29574 // define the action interface
29575 Roo.form.Action = function(form, options){
29576     this.form = form;
29577     this.options = options || {};
29578 };
29579 /**
29580  * Client Validation Failed
29581  * @const 
29582  */
29583 Roo.form.Action.CLIENT_INVALID = 'client';
29584 /**
29585  * Server Validation Failed
29586  * @const 
29587  */
29588 Roo.form.Action.SERVER_INVALID = 'server';
29589  /**
29590  * Connect to Server Failed
29591  * @const 
29592  */
29593 Roo.form.Action.CONNECT_FAILURE = 'connect';
29594 /**
29595  * Reading Data from Server Failed
29596  * @const 
29597  */
29598 Roo.form.Action.LOAD_FAILURE = 'load';
29599
29600 Roo.form.Action.prototype = {
29601     type : 'default',
29602     failureType : undefined,
29603     response : undefined,
29604     result : undefined,
29605
29606     // interface method
29607     run : function(options){
29608
29609     },
29610
29611     // interface method
29612     success : function(response){
29613
29614     },
29615
29616     // interface method
29617     handleResponse : function(response){
29618
29619     },
29620
29621     // default connection failure
29622     failure : function(response){
29623         
29624         this.response = response;
29625         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29626         this.form.afterAction(this, false);
29627     },
29628
29629     processResponse : function(response){
29630         this.response = response;
29631         if(!response.responseText){
29632             return true;
29633         }
29634         this.result = this.handleResponse(response);
29635         return this.result;
29636     },
29637
29638     // utility functions used internally
29639     getUrl : function(appendParams){
29640         var url = this.options.url || this.form.url || this.form.el.dom.action;
29641         if(appendParams){
29642             var p = this.getParams();
29643             if(p){
29644                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29645             }
29646         }
29647         return url;
29648     },
29649
29650     getMethod : function(){
29651         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29652     },
29653
29654     getParams : function(){
29655         var bp = this.form.baseParams;
29656         var p = this.options.params;
29657         if(p){
29658             if(typeof p == "object"){
29659                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29660             }else if(typeof p == 'string' && bp){
29661                 p += '&' + Roo.urlEncode(bp);
29662             }
29663         }else if(bp){
29664             p = Roo.urlEncode(bp);
29665         }
29666         return p;
29667     },
29668
29669     createCallback : function(){
29670         return {
29671             success: this.success,
29672             failure: this.failure,
29673             scope: this,
29674             timeout: (this.form.timeout*1000),
29675             upload: this.form.fileUpload ? this.success : undefined
29676         };
29677     }
29678 };
29679
29680 Roo.form.Action.Submit = function(form, options){
29681     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29682 };
29683
29684 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29685     type : 'submit',
29686
29687     haveProgress : false,
29688     uploadComplete : false,
29689     
29690     // uploadProgress indicator.
29691     uploadProgress : function()
29692     {
29693         if (!this.form.progressUrl) {
29694             return;
29695         }
29696         
29697         if (!this.haveProgress) {
29698             Roo.MessageBox.progress("Uploading", "Uploading");
29699         }
29700         if (this.uploadComplete) {
29701            Roo.MessageBox.hide();
29702            return;
29703         }
29704         
29705         this.haveProgress = true;
29706    
29707         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29708         
29709         var c = new Roo.data.Connection();
29710         c.request({
29711             url : this.form.progressUrl,
29712             params: {
29713                 id : uid
29714             },
29715             method: 'GET',
29716             success : function(req){
29717                //console.log(data);
29718                 var rdata = false;
29719                 var edata;
29720                 try  {
29721                    rdata = Roo.decode(req.responseText)
29722                 } catch (e) {
29723                     Roo.log("Invalid data from server..");
29724                     Roo.log(edata);
29725                     return;
29726                 }
29727                 if (!rdata || !rdata.success) {
29728                     Roo.log(rdata);
29729                     Roo.MessageBox.alert(Roo.encode(rdata));
29730                     return;
29731                 }
29732                 var data = rdata.data;
29733                 
29734                 if (this.uploadComplete) {
29735                    Roo.MessageBox.hide();
29736                    return;
29737                 }
29738                    
29739                 if (data){
29740                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29741                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29742                     );
29743                 }
29744                 this.uploadProgress.defer(2000,this);
29745             },
29746        
29747             failure: function(data) {
29748                 Roo.log('progress url failed ');
29749                 Roo.log(data);
29750             },
29751             scope : this
29752         });
29753            
29754     },
29755     
29756     
29757     run : function()
29758     {
29759         // run get Values on the form, so it syncs any secondary forms.
29760         this.form.getValues();
29761         
29762         var o = this.options;
29763         var method = this.getMethod();
29764         var isPost = method == 'POST';
29765         if(o.clientValidation === false || this.form.isValid()){
29766             
29767             if (this.form.progressUrl) {
29768                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29769                     (new Date() * 1) + '' + Math.random());
29770                     
29771             } 
29772             
29773             
29774             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29775                 form:this.form.el.dom,
29776                 url:this.getUrl(!isPost),
29777                 method: method,
29778                 params:isPost ? this.getParams() : null,
29779                 isUpload: this.form.fileUpload
29780             }));
29781             
29782             this.uploadProgress();
29783
29784         }else if (o.clientValidation !== false){ // client validation failed
29785             this.failureType = Roo.form.Action.CLIENT_INVALID;
29786             this.form.afterAction(this, false);
29787         }
29788     },
29789
29790     success : function(response)
29791     {
29792         this.uploadComplete= true;
29793         if (this.haveProgress) {
29794             Roo.MessageBox.hide();
29795         }
29796         
29797         
29798         var result = this.processResponse(response);
29799         if(result === true || result.success){
29800             this.form.afterAction(this, true);
29801             return;
29802         }
29803         if(result.errors){
29804             this.form.markInvalid(result.errors);
29805             this.failureType = Roo.form.Action.SERVER_INVALID;
29806         }
29807         this.form.afterAction(this, false);
29808     },
29809     failure : function(response)
29810     {
29811         this.uploadComplete= true;
29812         if (this.haveProgress) {
29813             Roo.MessageBox.hide();
29814         }
29815         
29816         this.response = response;
29817         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29818         this.form.afterAction(this, false);
29819     },
29820     
29821     handleResponse : function(response){
29822         if(this.form.errorReader){
29823             var rs = this.form.errorReader.read(response);
29824             var errors = [];
29825             if(rs.records){
29826                 for(var i = 0, len = rs.records.length; i < len; i++) {
29827                     var r = rs.records[i];
29828                     errors[i] = r.data;
29829                 }
29830             }
29831             if(errors.length < 1){
29832                 errors = null;
29833             }
29834             return {
29835                 success : rs.success,
29836                 errors : errors
29837             };
29838         }
29839         var ret = false;
29840         try {
29841             ret = Roo.decode(response.responseText);
29842         } catch (e) {
29843             ret = {
29844                 success: false,
29845                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29846                 errors : []
29847             };
29848         }
29849         return ret;
29850         
29851     }
29852 });
29853
29854
29855 Roo.form.Action.Load = function(form, options){
29856     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29857     this.reader = this.form.reader;
29858 };
29859
29860 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29861     type : 'load',
29862
29863     run : function(){
29864         
29865         Roo.Ajax.request(Roo.apply(
29866                 this.createCallback(), {
29867                     method:this.getMethod(),
29868                     url:this.getUrl(false),
29869                     params:this.getParams()
29870         }));
29871     },
29872
29873     success : function(response){
29874         
29875         var result = this.processResponse(response);
29876         if(result === true || !result.success || !result.data){
29877             this.failureType = Roo.form.Action.LOAD_FAILURE;
29878             this.form.afterAction(this, false);
29879             return;
29880         }
29881         this.form.clearInvalid();
29882         this.form.setValues(result.data);
29883         this.form.afterAction(this, true);
29884     },
29885
29886     handleResponse : function(response){
29887         if(this.form.reader){
29888             var rs = this.form.reader.read(response);
29889             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29890             return {
29891                 success : rs.success,
29892                 data : data
29893             };
29894         }
29895         return Roo.decode(response.responseText);
29896     }
29897 });
29898
29899 Roo.form.Action.ACTION_TYPES = {
29900     'load' : Roo.form.Action.Load,
29901     'submit' : Roo.form.Action.Submit
29902 };/*
29903  * Based on:
29904  * Ext JS Library 1.1.1
29905  * Copyright(c) 2006-2007, Ext JS, LLC.
29906  *
29907  * Originally Released Under LGPL - original licence link has changed is not relivant.
29908  *
29909  * Fork - LGPL
29910  * <script type="text/javascript">
29911  */
29912  
29913 /**
29914  * @class Roo.form.Layout
29915  * @extends Roo.Component
29916  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29917  * @constructor
29918  * @param {Object} config Configuration options
29919  */
29920 Roo.form.Layout = function(config){
29921     var xitems = [];
29922     if (config.items) {
29923         xitems = config.items;
29924         delete config.items;
29925     }
29926     Roo.form.Layout.superclass.constructor.call(this, config);
29927     this.stack = [];
29928     Roo.each(xitems, this.addxtype, this);
29929      
29930 };
29931
29932 Roo.extend(Roo.form.Layout, Roo.Component, {
29933     /**
29934      * @cfg {String/Object} autoCreate
29935      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29936      */
29937     /**
29938      * @cfg {String/Object/Function} style
29939      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29940      * a function which returns such a specification.
29941      */
29942     /**
29943      * @cfg {String} labelAlign
29944      * Valid values are "left," "top" and "right" (defaults to "left")
29945      */
29946     /**
29947      * @cfg {Number} labelWidth
29948      * Fixed width in pixels of all field labels (defaults to undefined)
29949      */
29950     /**
29951      * @cfg {Boolean} clear
29952      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29953      */
29954     clear : true,
29955     /**
29956      * @cfg {String} labelSeparator
29957      * The separator to use after field labels (defaults to ':')
29958      */
29959     labelSeparator : ':',
29960     /**
29961      * @cfg {Boolean} hideLabels
29962      * True to suppress the display of field labels in this layout (defaults to false)
29963      */
29964     hideLabels : false,
29965
29966     // private
29967     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29968     
29969     isLayout : true,
29970     
29971     // private
29972     onRender : function(ct, position){
29973         if(this.el){ // from markup
29974             this.el = Roo.get(this.el);
29975         }else {  // generate
29976             var cfg = this.getAutoCreate();
29977             this.el = ct.createChild(cfg, position);
29978         }
29979         if(this.style){
29980             this.el.applyStyles(this.style);
29981         }
29982         if(this.labelAlign){
29983             this.el.addClass('x-form-label-'+this.labelAlign);
29984         }
29985         if(this.hideLabels){
29986             this.labelStyle = "display:none";
29987             this.elementStyle = "padding-left:0;";
29988         }else{
29989             if(typeof this.labelWidth == 'number'){
29990                 this.labelStyle = "width:"+this.labelWidth+"px;";
29991                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29992             }
29993             if(this.labelAlign == 'top'){
29994                 this.labelStyle = "width:auto;";
29995                 this.elementStyle = "padding-left:0;";
29996             }
29997         }
29998         var stack = this.stack;
29999         var slen = stack.length;
30000         if(slen > 0){
30001             if(!this.fieldTpl){
30002                 var t = new Roo.Template(
30003                     '<div class="x-form-item {5}">',
30004                         '<label for="{0}" style="{2}">{1}{4}</label>',
30005                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30006                         '</div>',
30007                     '</div><div class="x-form-clear-left"></div>'
30008                 );
30009                 t.disableFormats = true;
30010                 t.compile();
30011                 Roo.form.Layout.prototype.fieldTpl = t;
30012             }
30013             for(var i = 0; i < slen; i++) {
30014                 if(stack[i].isFormField){
30015                     this.renderField(stack[i]);
30016                 }else{
30017                     this.renderComponent(stack[i]);
30018                 }
30019             }
30020         }
30021         if(this.clear){
30022             this.el.createChild({cls:'x-form-clear'});
30023         }
30024     },
30025
30026     // private
30027     renderField : function(f){
30028         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30029                f.id, //0
30030                f.fieldLabel, //1
30031                f.labelStyle||this.labelStyle||'', //2
30032                this.elementStyle||'', //3
30033                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30034                f.itemCls||this.itemCls||''  //5
30035        ], true).getPrevSibling());
30036     },
30037
30038     // private
30039     renderComponent : function(c){
30040         c.render(c.isLayout ? this.el : this.el.createChild());    
30041     },
30042     /**
30043      * Adds a object form elements (using the xtype property as the factory method.)
30044      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30045      * @param {Object} config 
30046      */
30047     addxtype : function(o)
30048     {
30049         // create the lement.
30050         o.form = this.form;
30051         var fe = Roo.factory(o, Roo.form);
30052         this.form.allItems.push(fe);
30053         this.stack.push(fe);
30054         
30055         if (fe.isFormField) {
30056             this.form.items.add(fe);
30057         }
30058          
30059         return fe;
30060     }
30061 });
30062
30063 /**
30064  * @class Roo.form.Column
30065  * @extends Roo.form.Layout
30066  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30067  * @constructor
30068  * @param {Object} config Configuration options
30069  */
30070 Roo.form.Column = function(config){
30071     Roo.form.Column.superclass.constructor.call(this, config);
30072 };
30073
30074 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30075     /**
30076      * @cfg {Number/String} width
30077      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30078      */
30079     /**
30080      * @cfg {String/Object} autoCreate
30081      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30082      */
30083
30084     // private
30085     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30086
30087     // private
30088     onRender : function(ct, position){
30089         Roo.form.Column.superclass.onRender.call(this, ct, position);
30090         if(this.width){
30091             this.el.setWidth(this.width);
30092         }
30093     }
30094 });
30095
30096
30097 /**
30098  * @class Roo.form.Row
30099  * @extends Roo.form.Layout
30100  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30101  * @constructor
30102  * @param {Object} config Configuration options
30103  */
30104
30105  
30106 Roo.form.Row = function(config){
30107     Roo.form.Row.superclass.constructor.call(this, config);
30108 };
30109  
30110 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30111       /**
30112      * @cfg {Number/String} width
30113      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30114      */
30115     /**
30116      * @cfg {Number/String} height
30117      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30118      */
30119     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30120     
30121     padWidth : 20,
30122     // private
30123     onRender : function(ct, position){
30124         //console.log('row render');
30125         if(!this.rowTpl){
30126             var t = new Roo.Template(
30127                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30128                     '<label for="{0}" style="{2}">{1}{4}</label>',
30129                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30130                     '</div>',
30131                 '</div>'
30132             );
30133             t.disableFormats = true;
30134             t.compile();
30135             Roo.form.Layout.prototype.rowTpl = t;
30136         }
30137         this.fieldTpl = this.rowTpl;
30138         
30139         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30140         var labelWidth = 100;
30141         
30142         if ((this.labelAlign != 'top')) {
30143             if (typeof this.labelWidth == 'number') {
30144                 labelWidth = this.labelWidth
30145             }
30146             this.padWidth =  20 + labelWidth;
30147             
30148         }
30149         
30150         Roo.form.Column.superclass.onRender.call(this, ct, position);
30151         if(this.width){
30152             this.el.setWidth(this.width);
30153         }
30154         if(this.height){
30155             this.el.setHeight(this.height);
30156         }
30157     },
30158     
30159     // private
30160     renderField : function(f){
30161         f.fieldEl = this.fieldTpl.append(this.el, [
30162                f.id, f.fieldLabel,
30163                f.labelStyle||this.labelStyle||'',
30164                this.elementStyle||'',
30165                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30166                f.itemCls||this.itemCls||'',
30167                f.width ? f.width + this.padWidth : 160 + this.padWidth
30168        ],true);
30169     }
30170 });
30171  
30172
30173 /**
30174  * @class Roo.form.FieldSet
30175  * @extends Roo.form.Layout
30176  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30177  * @constructor
30178  * @param {Object} config Configuration options
30179  */
30180 Roo.form.FieldSet = function(config){
30181     Roo.form.FieldSet.superclass.constructor.call(this, config);
30182 };
30183
30184 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30185     /**
30186      * @cfg {String} legend
30187      * The text to display as the legend for the FieldSet (defaults to '')
30188      */
30189     /**
30190      * @cfg {String/Object} autoCreate
30191      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30192      */
30193
30194     // private
30195     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30196
30197     // private
30198     onRender : function(ct, position){
30199         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30200         if(this.legend){
30201             this.setLegend(this.legend);
30202         }
30203     },
30204
30205     // private
30206     setLegend : function(text){
30207         if(this.rendered){
30208             this.el.child('legend').update(text);
30209         }
30210     }
30211 });/*
30212  * Based on:
30213  * Ext JS Library 1.1.1
30214  * Copyright(c) 2006-2007, Ext JS, LLC.
30215  *
30216  * Originally Released Under LGPL - original licence link has changed is not relivant.
30217  *
30218  * Fork - LGPL
30219  * <script type="text/javascript">
30220  */
30221 /**
30222  * @class Roo.form.VTypes
30223  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30224  * @singleton
30225  */
30226 Roo.form.VTypes = function(){
30227     // closure these in so they are only created once.
30228     var alpha = /^[a-zA-Z_]+$/;
30229     var alphanum = /^[a-zA-Z0-9_]+$/;
30230     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
30231     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30232
30233     // All these messages and functions are configurable
30234     return {
30235         /**
30236          * The function used to validate email addresses
30237          * @param {String} value The email address
30238          */
30239         'email' : function(v){
30240             return email.test(v);
30241         },
30242         /**
30243          * The error text to display when the email validation function returns false
30244          * @type String
30245          */
30246         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30247         /**
30248          * The keystroke filter mask to be applied on email input
30249          * @type RegExp
30250          */
30251         'emailMask' : /[a-z0-9_\.\-@]/i,
30252
30253         /**
30254          * The function used to validate URLs
30255          * @param {String} value The URL
30256          */
30257         'url' : function(v){
30258             return url.test(v);
30259         },
30260         /**
30261          * The error text to display when the url validation function returns false
30262          * @type String
30263          */
30264         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30265         
30266         /**
30267          * The function used to validate alpha values
30268          * @param {String} value The value
30269          */
30270         'alpha' : function(v){
30271             return alpha.test(v);
30272         },
30273         /**
30274          * The error text to display when the alpha validation function returns false
30275          * @type String
30276          */
30277         'alphaText' : 'This field should only contain letters and _',
30278         /**
30279          * The keystroke filter mask to be applied on alpha input
30280          * @type RegExp
30281          */
30282         'alphaMask' : /[a-z_]/i,
30283
30284         /**
30285          * The function used to validate alphanumeric values
30286          * @param {String} value The value
30287          */
30288         'alphanum' : function(v){
30289             return alphanum.test(v);
30290         },
30291         /**
30292          * The error text to display when the alphanumeric validation function returns false
30293          * @type String
30294          */
30295         'alphanumText' : 'This field should only contain letters, numbers and _',
30296         /**
30297          * The keystroke filter mask to be applied on alphanumeric input
30298          * @type RegExp
30299          */
30300         'alphanumMask' : /[a-z0-9_]/i
30301     };
30302 }();//<script type="text/javascript">
30303
30304 /**
30305  * @class Roo.form.FCKeditor
30306  * @extends Roo.form.TextArea
30307  * Wrapper around the FCKEditor http://www.fckeditor.net
30308  * @constructor
30309  * Creates a new FCKeditor
30310  * @param {Object} config Configuration options
30311  */
30312 Roo.form.FCKeditor = function(config){
30313     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30314     this.addEvents({
30315          /**
30316          * @event editorinit
30317          * Fired when the editor is initialized - you can add extra handlers here..
30318          * @param {FCKeditor} this
30319          * @param {Object} the FCK object.
30320          */
30321         editorinit : true
30322     });
30323     
30324     
30325 };
30326 Roo.form.FCKeditor.editors = { };
30327 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30328 {
30329     //defaultAutoCreate : {
30330     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30331     //},
30332     // private
30333     /**
30334      * @cfg {Object} fck options - see fck manual for details.
30335      */
30336     fckconfig : false,
30337     
30338     /**
30339      * @cfg {Object} fck toolbar set (Basic or Default)
30340      */
30341     toolbarSet : 'Basic',
30342     /**
30343      * @cfg {Object} fck BasePath
30344      */ 
30345     basePath : '/fckeditor/',
30346     
30347     
30348     frame : false,
30349     
30350     value : '',
30351     
30352    
30353     onRender : function(ct, position)
30354     {
30355         if(!this.el){
30356             this.defaultAutoCreate = {
30357                 tag: "textarea",
30358                 style:"width:300px;height:60px;",
30359                 autocomplete: "new-password"
30360             };
30361         }
30362         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30363         /*
30364         if(this.grow){
30365             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30366             if(this.preventScrollbars){
30367                 this.el.setStyle("overflow", "hidden");
30368             }
30369             this.el.setHeight(this.growMin);
30370         }
30371         */
30372         //console.log('onrender' + this.getId() );
30373         Roo.form.FCKeditor.editors[this.getId()] = this;
30374          
30375
30376         this.replaceTextarea() ;
30377         
30378     },
30379     
30380     getEditor : function() {
30381         return this.fckEditor;
30382     },
30383     /**
30384      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30385      * @param {Mixed} value The value to set
30386      */
30387     
30388     
30389     setValue : function(value)
30390     {
30391         //console.log('setValue: ' + value);
30392         
30393         if(typeof(value) == 'undefined') { // not sure why this is happending...
30394             return;
30395         }
30396         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30397         
30398         //if(!this.el || !this.getEditor()) {
30399         //    this.value = value;
30400             //this.setValue.defer(100,this,[value]);    
30401         //    return;
30402         //} 
30403         
30404         if(!this.getEditor()) {
30405             return;
30406         }
30407         
30408         this.getEditor().SetData(value);
30409         
30410         //
30411
30412     },
30413
30414     /**
30415      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30416      * @return {Mixed} value The field value
30417      */
30418     getValue : function()
30419     {
30420         
30421         if (this.frame && this.frame.dom.style.display == 'none') {
30422             return Roo.form.FCKeditor.superclass.getValue.call(this);
30423         }
30424         
30425         if(!this.el || !this.getEditor()) {
30426            
30427            // this.getValue.defer(100,this); 
30428             return this.value;
30429         }
30430        
30431         
30432         var value=this.getEditor().GetData();
30433         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30434         return Roo.form.FCKeditor.superclass.getValue.call(this);
30435         
30436
30437     },
30438
30439     /**
30440      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30441      * @return {Mixed} value The field value
30442      */
30443     getRawValue : function()
30444     {
30445         if (this.frame && this.frame.dom.style.display == 'none') {
30446             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30447         }
30448         
30449         if(!this.el || !this.getEditor()) {
30450             //this.getRawValue.defer(100,this); 
30451             return this.value;
30452             return;
30453         }
30454         
30455         
30456         
30457         var value=this.getEditor().GetData();
30458         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30459         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30460          
30461     },
30462     
30463     setSize : function(w,h) {
30464         
30465         
30466         
30467         //if (this.frame && this.frame.dom.style.display == 'none') {
30468         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30469         //    return;
30470         //}
30471         //if(!this.el || !this.getEditor()) {
30472         //    this.setSize.defer(100,this, [w,h]); 
30473         //    return;
30474         //}
30475         
30476         
30477         
30478         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30479         
30480         this.frame.dom.setAttribute('width', w);
30481         this.frame.dom.setAttribute('height', h);
30482         this.frame.setSize(w,h);
30483         
30484     },
30485     
30486     toggleSourceEdit : function(value) {
30487         
30488       
30489          
30490         this.el.dom.style.display = value ? '' : 'none';
30491         this.frame.dom.style.display = value ?  'none' : '';
30492         
30493     },
30494     
30495     
30496     focus: function(tag)
30497     {
30498         if (this.frame.dom.style.display == 'none') {
30499             return Roo.form.FCKeditor.superclass.focus.call(this);
30500         }
30501         if(!this.el || !this.getEditor()) {
30502             this.focus.defer(100,this, [tag]); 
30503             return;
30504         }
30505         
30506         
30507         
30508         
30509         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30510         this.getEditor().Focus();
30511         if (tgs.length) {
30512             if (!this.getEditor().Selection.GetSelection()) {
30513                 this.focus.defer(100,this, [tag]); 
30514                 return;
30515             }
30516             
30517             
30518             var r = this.getEditor().EditorDocument.createRange();
30519             r.setStart(tgs[0],0);
30520             r.setEnd(tgs[0],0);
30521             this.getEditor().Selection.GetSelection().removeAllRanges();
30522             this.getEditor().Selection.GetSelection().addRange(r);
30523             this.getEditor().Focus();
30524         }
30525         
30526     },
30527     
30528     
30529     
30530     replaceTextarea : function()
30531     {
30532         if ( document.getElementById( this.getId() + '___Frame' ) )
30533             return ;
30534         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30535         //{
30536             // We must check the elements firstly using the Id and then the name.
30537         var oTextarea = document.getElementById( this.getId() );
30538         
30539         var colElementsByName = document.getElementsByName( this.getId() ) ;
30540          
30541         oTextarea.style.display = 'none' ;
30542
30543         if ( oTextarea.tabIndex ) {            
30544             this.TabIndex = oTextarea.tabIndex ;
30545         }
30546         
30547         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30548         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30549         this.frame = Roo.get(this.getId() + '___Frame')
30550     },
30551     
30552     _getConfigHtml : function()
30553     {
30554         var sConfig = '' ;
30555
30556         for ( var o in this.fckconfig ) {
30557             sConfig += sConfig.length > 0  ? '&amp;' : '';
30558             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30559         }
30560
30561         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30562     },
30563     
30564     
30565     _getIFrameHtml : function()
30566     {
30567         var sFile = 'fckeditor.html' ;
30568         /* no idea what this is about..
30569         try
30570         {
30571             if ( (/fcksource=true/i).test( window.top.location.search ) )
30572                 sFile = 'fckeditor.original.html' ;
30573         }
30574         catch (e) { 
30575         */
30576
30577         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30578         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30579         
30580         
30581         var html = '<iframe id="' + this.getId() +
30582             '___Frame" src="' + sLink +
30583             '" width="' + this.width +
30584             '" height="' + this.height + '"' +
30585             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30586             ' frameborder="0" scrolling="no"></iframe>' ;
30587
30588         return html ;
30589     },
30590     
30591     _insertHtmlBefore : function( html, element )
30592     {
30593         if ( element.insertAdjacentHTML )       {
30594             // IE
30595             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30596         } else { // Gecko
30597             var oRange = document.createRange() ;
30598             oRange.setStartBefore( element ) ;
30599             var oFragment = oRange.createContextualFragment( html );
30600             element.parentNode.insertBefore( oFragment, element ) ;
30601         }
30602     }
30603     
30604     
30605   
30606     
30607     
30608     
30609     
30610
30611 });
30612
30613 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30614
30615 function FCKeditor_OnComplete(editorInstance){
30616     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30617     f.fckEditor = editorInstance;
30618     //console.log("loaded");
30619     f.fireEvent('editorinit', f, editorInstance);
30620
30621   
30622
30623  
30624
30625
30626
30627
30628
30629
30630
30631
30632
30633
30634
30635
30636
30637
30638
30639 //<script type="text/javascript">
30640 /**
30641  * @class Roo.form.GridField
30642  * @extends Roo.form.Field
30643  * Embed a grid (or editable grid into a form)
30644  * STATUS ALPHA
30645  * 
30646  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30647  * it needs 
30648  * xgrid.store = Roo.data.Store
30649  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30650  * xgrid.store.reader = Roo.data.JsonReader 
30651  * 
30652  * 
30653  * @constructor
30654  * Creates a new GridField
30655  * @param {Object} config Configuration options
30656  */
30657 Roo.form.GridField = function(config){
30658     Roo.form.GridField.superclass.constructor.call(this, config);
30659      
30660 };
30661
30662 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30663     /**
30664      * @cfg {Number} width  - used to restrict width of grid..
30665      */
30666     width : 100,
30667     /**
30668      * @cfg {Number} height - used to restrict height of grid..
30669      */
30670     height : 50,
30671      /**
30672      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30673          * 
30674          *}
30675      */
30676     xgrid : false, 
30677     /**
30678      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30679      * {tag: "input", type: "checkbox", autocomplete: "off"})
30680      */
30681    // defaultAutoCreate : { tag: 'div' },
30682     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30683     /**
30684      * @cfg {String} addTitle Text to include for adding a title.
30685      */
30686     addTitle : false,
30687     //
30688     onResize : function(){
30689         Roo.form.Field.superclass.onResize.apply(this, arguments);
30690     },
30691
30692     initEvents : function(){
30693         // Roo.form.Checkbox.superclass.initEvents.call(this);
30694         // has no events...
30695        
30696     },
30697
30698
30699     getResizeEl : function(){
30700         return this.wrap;
30701     },
30702
30703     getPositionEl : function(){
30704         return this.wrap;
30705     },
30706
30707     // private
30708     onRender : function(ct, position){
30709         
30710         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30711         var style = this.style;
30712         delete this.style;
30713         
30714         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30715         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30716         this.viewEl = this.wrap.createChild({ tag: 'div' });
30717         if (style) {
30718             this.viewEl.applyStyles(style);
30719         }
30720         if (this.width) {
30721             this.viewEl.setWidth(this.width);
30722         }
30723         if (this.height) {
30724             this.viewEl.setHeight(this.height);
30725         }
30726         //if(this.inputValue !== undefined){
30727         //this.setValue(this.value);
30728         
30729         
30730         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30731         
30732         
30733         this.grid.render();
30734         this.grid.getDataSource().on('remove', this.refreshValue, this);
30735         this.grid.getDataSource().on('update', this.refreshValue, this);
30736         this.grid.on('afteredit', this.refreshValue, this);
30737  
30738     },
30739      
30740     
30741     /**
30742      * Sets the value of the item. 
30743      * @param {String} either an object  or a string..
30744      */
30745     setValue : function(v){
30746         //this.value = v;
30747         v = v || []; // empty set..
30748         // this does not seem smart - it really only affects memoryproxy grids..
30749         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30750             var ds = this.grid.getDataSource();
30751             // assumes a json reader..
30752             var data = {}
30753             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30754             ds.loadData( data);
30755         }
30756         // clear selection so it does not get stale.
30757         if (this.grid.sm) { 
30758             this.grid.sm.clearSelections();
30759         }
30760         
30761         Roo.form.GridField.superclass.setValue.call(this, v);
30762         this.refreshValue();
30763         // should load data in the grid really....
30764     },
30765     
30766     // private
30767     refreshValue: function() {
30768          var val = [];
30769         this.grid.getDataSource().each(function(r) {
30770             val.push(r.data);
30771         });
30772         this.el.dom.value = Roo.encode(val);
30773     }
30774     
30775      
30776     
30777     
30778 });/*
30779  * Based on:
30780  * Ext JS Library 1.1.1
30781  * Copyright(c) 2006-2007, Ext JS, LLC.
30782  *
30783  * Originally Released Under LGPL - original licence link has changed is not relivant.
30784  *
30785  * Fork - LGPL
30786  * <script type="text/javascript">
30787  */
30788 /**
30789  * @class Roo.form.DisplayField
30790  * @extends Roo.form.Field
30791  * A generic Field to display non-editable data.
30792  * @constructor
30793  * Creates a new Display Field item.
30794  * @param {Object} config Configuration options
30795  */
30796 Roo.form.DisplayField = function(config){
30797     Roo.form.DisplayField.superclass.constructor.call(this, config);
30798     
30799 };
30800
30801 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30802     inputType:      'hidden',
30803     allowBlank:     true,
30804     readOnly:         true,
30805     
30806  
30807     /**
30808      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30809      */
30810     focusClass : undefined,
30811     /**
30812      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30813      */
30814     fieldClass: 'x-form-field',
30815     
30816      /**
30817      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30818      */
30819     valueRenderer: undefined,
30820     
30821     width: 100,
30822     /**
30823      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30824      * {tag: "input", type: "checkbox", autocomplete: "off"})
30825      */
30826      
30827  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30828
30829     onResize : function(){
30830         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30831         
30832     },
30833
30834     initEvents : function(){
30835         // Roo.form.Checkbox.superclass.initEvents.call(this);
30836         // has no events...
30837        
30838     },
30839
30840
30841     getResizeEl : function(){
30842         return this.wrap;
30843     },
30844
30845     getPositionEl : function(){
30846         return this.wrap;
30847     },
30848
30849     // private
30850     onRender : function(ct, position){
30851         
30852         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30853         //if(this.inputValue !== undefined){
30854         this.wrap = this.el.wrap();
30855         
30856         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30857         
30858         if (this.bodyStyle) {
30859             this.viewEl.applyStyles(this.bodyStyle);
30860         }
30861         //this.viewEl.setStyle('padding', '2px');
30862         
30863         this.setValue(this.value);
30864         
30865     },
30866 /*
30867     // private
30868     initValue : Roo.emptyFn,
30869
30870   */
30871
30872         // private
30873     onClick : function(){
30874         
30875     },
30876
30877     /**
30878      * Sets the checked state of the checkbox.
30879      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30880      */
30881     setValue : function(v){
30882         this.value = v;
30883         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30884         // this might be called before we have a dom element..
30885         if (!this.viewEl) {
30886             return;
30887         }
30888         this.viewEl.dom.innerHTML = html;
30889         Roo.form.DisplayField.superclass.setValue.call(this, v);
30890
30891     }
30892 });/*
30893  * 
30894  * Licence- LGPL
30895  * 
30896  */
30897
30898 /**
30899  * @class Roo.form.DayPicker
30900  * @extends Roo.form.Field
30901  * A Day picker show [M] [T] [W] ....
30902  * @constructor
30903  * Creates a new Day Picker
30904  * @param {Object} config Configuration options
30905  */
30906 Roo.form.DayPicker= function(config){
30907     Roo.form.DayPicker.superclass.constructor.call(this, config);
30908      
30909 };
30910
30911 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30912     /**
30913      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30914      */
30915     focusClass : undefined,
30916     /**
30917      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30918      */
30919     fieldClass: "x-form-field",
30920    
30921     /**
30922      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30923      * {tag: "input", type: "checkbox", autocomplete: "off"})
30924      */
30925     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
30926     
30927    
30928     actionMode : 'viewEl', 
30929     //
30930     // private
30931  
30932     inputType : 'hidden',
30933     
30934      
30935     inputElement: false, // real input element?
30936     basedOn: false, // ????
30937     
30938     isFormField: true, // not sure where this is needed!!!!
30939
30940     onResize : function(){
30941         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30942         if(!this.boxLabel){
30943             this.el.alignTo(this.wrap, 'c-c');
30944         }
30945     },
30946
30947     initEvents : function(){
30948         Roo.form.Checkbox.superclass.initEvents.call(this);
30949         this.el.on("click", this.onClick,  this);
30950         this.el.on("change", this.onClick,  this);
30951     },
30952
30953
30954     getResizeEl : function(){
30955         return this.wrap;
30956     },
30957
30958     getPositionEl : function(){
30959         return this.wrap;
30960     },
30961
30962     
30963     // private
30964     onRender : function(ct, position){
30965         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30966        
30967         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30968         
30969         var r1 = '<table><tr>';
30970         var r2 = '<tr class="x-form-daypick-icons">';
30971         for (var i=0; i < 7; i++) {
30972             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30973             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30974         }
30975         
30976         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30977         viewEl.select('img').on('click', this.onClick, this);
30978         this.viewEl = viewEl;   
30979         
30980         
30981         // this will not work on Chrome!!!
30982         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30983         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30984         
30985         
30986           
30987
30988     },
30989
30990     // private
30991     initValue : Roo.emptyFn,
30992
30993     /**
30994      * Returns the checked state of the checkbox.
30995      * @return {Boolean} True if checked, else false
30996      */
30997     getValue : function(){
30998         return this.el.dom.value;
30999         
31000     },
31001
31002         // private
31003     onClick : function(e){ 
31004         //this.setChecked(!this.checked);
31005         Roo.get(e.target).toggleClass('x-menu-item-checked');
31006         this.refreshValue();
31007         //if(this.el.dom.checked != this.checked){
31008         //    this.setValue(this.el.dom.checked);
31009        // }
31010     },
31011     
31012     // private
31013     refreshValue : function()
31014     {
31015         var val = '';
31016         this.viewEl.select('img',true).each(function(e,i,n)  {
31017             val += e.is(".x-menu-item-checked") ? String(n) : '';
31018         });
31019         this.setValue(val, true);
31020     },
31021
31022     /**
31023      * Sets the checked state of the checkbox.
31024      * On is always based on a string comparison between inputValue and the param.
31025      * @param {Boolean/String} value - the value to set 
31026      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31027      */
31028     setValue : function(v,suppressEvent){
31029         if (!this.el.dom) {
31030             return;
31031         }
31032         var old = this.el.dom.value ;
31033         this.el.dom.value = v;
31034         if (suppressEvent) {
31035             return ;
31036         }
31037          
31038         // update display..
31039         this.viewEl.select('img',true).each(function(e,i,n)  {
31040             
31041             var on = e.is(".x-menu-item-checked");
31042             var newv = v.indexOf(String(n)) > -1;
31043             if (on != newv) {
31044                 e.toggleClass('x-menu-item-checked');
31045             }
31046             
31047         });
31048         
31049         
31050         this.fireEvent('change', this, v, old);
31051         
31052         
31053     },
31054    
31055     // handle setting of hidden value by some other method!!?!?
31056     setFromHidden: function()
31057     {
31058         if(!this.el){
31059             return;
31060         }
31061         //console.log("SET FROM HIDDEN");
31062         //alert('setFrom hidden');
31063         this.setValue(this.el.dom.value);
31064     },
31065     
31066     onDestroy : function()
31067     {
31068         if(this.viewEl){
31069             Roo.get(this.viewEl).remove();
31070         }
31071          
31072         Roo.form.DayPicker.superclass.onDestroy.call(this);
31073     }
31074
31075 });/*
31076  * RooJS Library 1.1.1
31077  * Copyright(c) 2008-2011  Alan Knowles
31078  *
31079  * License - LGPL
31080  */
31081  
31082
31083 /**
31084  * @class Roo.form.ComboCheck
31085  * @extends Roo.form.ComboBox
31086  * A combobox for multiple select items.
31087  *
31088  * FIXME - could do with a reset button..
31089  * 
31090  * @constructor
31091  * Create a new ComboCheck
31092  * @param {Object} config Configuration options
31093  */
31094 Roo.form.ComboCheck = function(config){
31095     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31096     // should verify some data...
31097     // like
31098     // hiddenName = required..
31099     // displayField = required
31100     // valudField == required
31101     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31102     var _t = this;
31103     Roo.each(req, function(e) {
31104         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31105             throw "Roo.form.ComboCheck : missing value for: " + e;
31106         }
31107     });
31108     
31109     
31110 };
31111
31112 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31113      
31114      
31115     editable : false,
31116      
31117     selectedClass: 'x-menu-item-checked', 
31118     
31119     // private
31120     onRender : function(ct, position){
31121         var _t = this;
31122         
31123         
31124         
31125         if(!this.tpl){
31126             var cls = 'x-combo-list';
31127
31128             
31129             this.tpl =  new Roo.Template({
31130                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31131                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31132                    '<span>{' + this.displayField + '}</span>' +
31133                     '</div>' 
31134                 
31135             });
31136         }
31137  
31138         
31139         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31140         this.view.singleSelect = false;
31141         this.view.multiSelect = true;
31142         this.view.toggleSelect = true;
31143         this.pageTb.add(new Roo.Toolbar.Fill(), {
31144             
31145             text: 'Done',
31146             handler: function()
31147             {
31148                 _t.collapse();
31149             }
31150         });
31151     },
31152     
31153     onViewOver : function(e, t){
31154         // do nothing...
31155         return;
31156         
31157     },
31158     
31159     onViewClick : function(doFocus,index){
31160         return;
31161         
31162     },
31163     select: function () {
31164         //Roo.log("SELECT CALLED");
31165     },
31166      
31167     selectByValue : function(xv, scrollIntoView){
31168         var ar = this.getValueArray();
31169         var sels = [];
31170         
31171         Roo.each(ar, function(v) {
31172             if(v === undefined || v === null){
31173                 return;
31174             }
31175             var r = this.findRecord(this.valueField, v);
31176             if(r){
31177                 sels.push(this.store.indexOf(r))
31178                 
31179             }
31180         },this);
31181         this.view.select(sels);
31182         return false;
31183     },
31184     
31185     
31186     
31187     onSelect : function(record, index){
31188        // Roo.log("onselect Called");
31189        // this is only called by the clear button now..
31190         this.view.clearSelections();
31191         this.setValue('[]');
31192         if (this.value != this.valueBefore) {
31193             this.fireEvent('change', this, this.value, this.valueBefore);
31194             this.valueBefore = this.value;
31195         }
31196     },
31197     getValueArray : function()
31198     {
31199         var ar = [] ;
31200         
31201         try {
31202             //Roo.log(this.value);
31203             if (typeof(this.value) == 'undefined') {
31204                 return [];
31205             }
31206             var ar = Roo.decode(this.value);
31207             return  ar instanceof Array ? ar : []; //?? valid?
31208             
31209         } catch(e) {
31210             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31211             return [];
31212         }
31213          
31214     },
31215     expand : function ()
31216     {
31217         
31218         Roo.form.ComboCheck.superclass.expand.call(this);
31219         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31220         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31221         
31222
31223     },
31224     
31225     collapse : function(){
31226         Roo.form.ComboCheck.superclass.collapse.call(this);
31227         var sl = this.view.getSelectedIndexes();
31228         var st = this.store;
31229         var nv = [];
31230         var tv = [];
31231         var r;
31232         Roo.each(sl, function(i) {
31233             r = st.getAt(i);
31234             nv.push(r.get(this.valueField));
31235         },this);
31236         this.setValue(Roo.encode(nv));
31237         if (this.value != this.valueBefore) {
31238
31239             this.fireEvent('change', this, this.value, this.valueBefore);
31240             this.valueBefore = this.value;
31241         }
31242         
31243     },
31244     
31245     setValue : function(v){
31246         // Roo.log(v);
31247         this.value = v;
31248         
31249         var vals = this.getValueArray();
31250         var tv = [];
31251         Roo.each(vals, function(k) {
31252             var r = this.findRecord(this.valueField, k);
31253             if(r){
31254                 tv.push(r.data[this.displayField]);
31255             }else if(this.valueNotFoundText !== undefined){
31256                 tv.push( this.valueNotFoundText );
31257             }
31258         },this);
31259        // Roo.log(tv);
31260         
31261         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31262         this.hiddenField.value = v;
31263         this.value = v;
31264     }
31265     
31266 });/*
31267  * Based on:
31268  * Ext JS Library 1.1.1
31269  * Copyright(c) 2006-2007, Ext JS, LLC.
31270  *
31271  * Originally Released Under LGPL - original licence link has changed is not relivant.
31272  *
31273  * Fork - LGPL
31274  * <script type="text/javascript">
31275  */
31276  
31277 /**
31278  * @class Roo.form.Signature
31279  * @extends Roo.form.Field
31280  * Signature field.  
31281  * @constructor
31282  * 
31283  * @param {Object} config Configuration options
31284  */
31285
31286 Roo.form.Signature = function(config){
31287     Roo.form.Signature.superclass.constructor.call(this, config);
31288     
31289     this.addEvents({// not in used??
31290          /**
31291          * @event confirm
31292          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31293              * @param {Roo.form.Signature} combo This combo box
31294              */
31295         'confirm' : true,
31296         /**
31297          * @event reset
31298          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31299              * @param {Roo.form.ComboBox} combo This combo box
31300              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31301              */
31302         'reset' : true
31303     });
31304 };
31305
31306 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31307     /**
31308      * @cfg {Object} labels Label to use when rendering a form.
31309      * defaults to 
31310      * labels : { 
31311      *      clear : "Clear",
31312      *      confirm : "Confirm"
31313      *  }
31314      */
31315     labels : { 
31316         clear : "Clear",
31317         confirm : "Confirm"
31318     },
31319     /**
31320      * @cfg {Number} width The signature panel width (defaults to 300)
31321      */
31322     width: 300,
31323     /**
31324      * @cfg {Number} height The signature panel height (defaults to 100)
31325      */
31326     height : 100,
31327     /**
31328      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31329      */
31330     allowBlank : false,
31331     
31332     //private
31333     // {Object} signPanel The signature SVG panel element (defaults to {})
31334     signPanel : {},
31335     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31336     isMouseDown : false,
31337     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31338     isConfirmed : false,
31339     // {String} signatureTmp SVG mapping string (defaults to empty string)
31340     signatureTmp : '',
31341     
31342     
31343     defaultAutoCreate : { // modified by initCompnoent..
31344         tag: "input",
31345         type:"hidden"
31346     },
31347
31348     // private
31349     onRender : function(ct, position){
31350         
31351         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31352         
31353         this.wrap = this.el.wrap({
31354             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31355         });
31356         
31357         this.createToolbar(this);
31358         this.signPanel = this.wrap.createChild({
31359                 tag: 'div',
31360                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31361             }, this.el
31362         );
31363             
31364         this.svgID = Roo.id();
31365         this.svgEl = this.signPanel.createChild({
31366               xmlns : 'http://www.w3.org/2000/svg',
31367               tag : 'svg',
31368               id : this.svgID + "-svg",
31369               width: this.width,
31370               height: this.height,
31371               viewBox: '0 0 '+this.width+' '+this.height,
31372               cn : [
31373                 {
31374                     tag: "rect",
31375                     id: this.svgID + "-svg-r",
31376                     width: this.width,
31377                     height: this.height,
31378                     fill: "#ffa"
31379                 },
31380                 {
31381                     tag: "line",
31382                     id: this.svgID + "-svg-l",
31383                     x1: "0", // start
31384                     y1: (this.height*0.8), // start set the line in 80% of height
31385                     x2: this.width, // end
31386                     y2: (this.height*0.8), // end set the line in 80% of height
31387                     'stroke': "#666",
31388                     'stroke-width': "1",
31389                     'stroke-dasharray': "3",
31390                     'shape-rendering': "crispEdges",
31391                     'pointer-events': "none"
31392                 },
31393                 {
31394                     tag: "path",
31395                     id: this.svgID + "-svg-p",
31396                     'stroke': "navy",
31397                     'stroke-width': "3",
31398                     'fill': "none",
31399                     'pointer-events': 'none'
31400                 }
31401               ]
31402         });
31403         this.createSVG();
31404         this.svgBox = this.svgEl.dom.getScreenCTM();
31405     },
31406     createSVG : function(){ 
31407         var svg = this.signPanel;
31408         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31409         var t = this;
31410
31411         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31412         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31413         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31414         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31415         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31416         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31417         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31418         
31419     },
31420     isTouchEvent : function(e){
31421         return e.type.match(/^touch/);
31422     },
31423     getCoords : function (e) {
31424         var pt    = this.svgEl.dom.createSVGPoint();
31425         pt.x = e.clientX; 
31426         pt.y = e.clientY;
31427         if (this.isTouchEvent(e)) {
31428             pt.x =  e.targetTouches[0].clientX 
31429             pt.y = e.targetTouches[0].clientY;
31430         }
31431         var a = this.svgEl.dom.getScreenCTM();
31432         var b = a.inverse();
31433         var mx = pt.matrixTransform(b);
31434         return mx.x + ',' + mx.y;
31435     },
31436     //mouse event headler 
31437     down : function (e) {
31438         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31439         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31440         
31441         this.isMouseDown = true;
31442         
31443         e.preventDefault();
31444     },
31445     move : function (e) {
31446         if (this.isMouseDown) {
31447             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31448             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31449         }
31450         
31451         e.preventDefault();
31452     },
31453     up : function (e) {
31454         this.isMouseDown = false;
31455         var sp = this.signatureTmp.split(' ');
31456         
31457         if(sp.length > 1){
31458             if(!sp[sp.length-2].match(/^L/)){
31459                 sp.pop();
31460                 sp.pop();
31461                 sp.push("");
31462                 this.signatureTmp = sp.join(" ");
31463             }
31464         }
31465         if(this.getValue() != this.signatureTmp){
31466             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31467             this.isConfirmed = false;
31468         }
31469         e.preventDefault();
31470     },
31471     
31472     /**
31473      * Protected method that will not generally be called directly. It
31474      * is called when the editor creates its toolbar. Override this method if you need to
31475      * add custom toolbar buttons.
31476      * @param {HtmlEditor} editor
31477      */
31478     createToolbar : function(editor){
31479          function btn(id, toggle, handler){
31480             var xid = fid + '-'+ id ;
31481             return {
31482                 id : xid,
31483                 cmd : id,
31484                 cls : 'x-btn-icon x-edit-'+id,
31485                 enableToggle:toggle !== false,
31486                 scope: editor, // was editor...
31487                 handler:handler||editor.relayBtnCmd,
31488                 clickEvent:'mousedown',
31489                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31490                 tabIndex:-1
31491             };
31492         }
31493         
31494         
31495         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31496         this.tb = tb;
31497         this.tb.add(
31498            {
31499                 cls : ' x-signature-btn x-signature-'+id,
31500                 scope: editor, // was editor...
31501                 handler: this.reset,
31502                 clickEvent:'mousedown',
31503                 text: this.labels.clear
31504             },
31505             {
31506                  xtype : 'Fill',
31507                  xns: Roo.Toolbar
31508             }, 
31509             {
31510                 cls : '  x-signature-btn x-signature-'+id,
31511                 scope: editor, // was editor...
31512                 handler: this.confirmHandler,
31513                 clickEvent:'mousedown',
31514                 text: this.labels.confirm
31515             }
31516         );
31517     
31518     },
31519     //public
31520     /**
31521      * when user is clicked confirm then show this image.....
31522      * 
31523      * @return {String} Image Data URI
31524      */
31525     getImageDataURI : function(){
31526         var svg = this.svgEl.dom.parentNode.innerHTML;
31527         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31528         return src; 
31529     },
31530     /**
31531      * 
31532      * @return {Boolean} this.isConfirmed
31533      */
31534     getConfirmed : function(){
31535         return this.isConfirmed;
31536     },
31537     /**
31538      * 
31539      * @return {Number} this.width
31540      */
31541     getWidth : function(){
31542         return this.width;
31543     },
31544     /**
31545      * 
31546      * @return {Number} this.height
31547      */
31548     getHeight : function(){
31549         return this.height;
31550     },
31551     // private
31552     getSignature : function(){
31553         return this.signatureTmp;
31554     },
31555     // private
31556     reset : function(){
31557         this.signatureTmp = '';
31558         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31559         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31560         this.isConfirmed = false;
31561         Roo.form.Signature.superclass.reset.call(this);
31562     },
31563     setSignature : function(s){
31564         this.signatureTmp = s;
31565         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31566         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31567         this.setValue(s);
31568         this.isConfirmed = false;
31569         Roo.form.Signature.superclass.reset.call(this);
31570     }, 
31571     test : function(){
31572 //        Roo.log(this.signPanel.dom.contentWindow.up())
31573     },
31574     //private
31575     setConfirmed : function(){
31576         
31577         
31578         
31579 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31580     },
31581     // private
31582     confirmHandler : function(){
31583         if(!this.getSignature()){
31584             return;
31585         }
31586         
31587         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31588         this.setValue(this.getSignature());
31589         this.isConfirmed = true;
31590         
31591         this.fireEvent('confirm', this);
31592     },
31593     // private
31594     // Subclasses should provide the validation implementation by overriding this
31595     validateValue : function(value){
31596         if(this.allowBlank){
31597             return true;
31598         }
31599         
31600         if(this.isConfirmed){
31601             return true;
31602         }
31603         return false;
31604     }
31605 });/*
31606  * Based on:
31607  * Ext JS Library 1.1.1
31608  * Copyright(c) 2006-2007, Ext JS, LLC.
31609  *
31610  * Originally Released Under LGPL - original licence link has changed is not relivant.
31611  *
31612  * Fork - LGPL
31613  * <script type="text/javascript">
31614  */
31615  
31616
31617 /**
31618  * @class Roo.form.ComboBox
31619  * @extends Roo.form.TriggerField
31620  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31621  * @constructor
31622  * Create a new ComboBox.
31623  * @param {Object} config Configuration options
31624  */
31625 Roo.form.Select = function(config){
31626     Roo.form.Select.superclass.constructor.call(this, config);
31627      
31628 };
31629
31630 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31631     /**
31632      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31633      */
31634     /**
31635      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31636      * rendering into an Roo.Editor, defaults to false)
31637      */
31638     /**
31639      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31640      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31641      */
31642     /**
31643      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31644      */
31645     /**
31646      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31647      * the dropdown list (defaults to undefined, with no header element)
31648      */
31649
31650      /**
31651      * @cfg {String/Roo.Template} tpl The template to use to render the output
31652      */
31653      
31654     // private
31655     defaultAutoCreate : {tag: "select"  },
31656     /**
31657      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31658      */
31659     listWidth: undefined,
31660     /**
31661      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31662      * mode = 'remote' or 'text' if mode = 'local')
31663      */
31664     displayField: undefined,
31665     /**
31666      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31667      * mode = 'remote' or 'value' if mode = 'local'). 
31668      * Note: use of a valueField requires the user make a selection
31669      * in order for a value to be mapped.
31670      */
31671     valueField: undefined,
31672     
31673     
31674     /**
31675      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31676      * field's data value (defaults to the underlying DOM element's name)
31677      */
31678     hiddenName: undefined,
31679     /**
31680      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31681      */
31682     listClass: '',
31683     /**
31684      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31685      */
31686     selectedClass: 'x-combo-selected',
31687     /**
31688      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31689      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31690      * which displays a downward arrow icon).
31691      */
31692     triggerClass : 'x-form-arrow-trigger',
31693     /**
31694      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31695      */
31696     shadow:'sides',
31697     /**
31698      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31699      * anchor positions (defaults to 'tl-bl')
31700      */
31701     listAlign: 'tl-bl?',
31702     /**
31703      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31704      */
31705     maxHeight: 300,
31706     /**
31707      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31708      * query specified by the allQuery config option (defaults to 'query')
31709      */
31710     triggerAction: 'query',
31711     /**
31712      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31713      * (defaults to 4, does not apply if editable = false)
31714      */
31715     minChars : 4,
31716     /**
31717      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31718      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31719      */
31720     typeAhead: false,
31721     /**
31722      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31723      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31724      */
31725     queryDelay: 500,
31726     /**
31727      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31728      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31729      */
31730     pageSize: 0,
31731     /**
31732      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31733      * when editable = true (defaults to false)
31734      */
31735     selectOnFocus:false,
31736     /**
31737      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31738      */
31739     queryParam: 'query',
31740     /**
31741      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31742      * when mode = 'remote' (defaults to 'Loading...')
31743      */
31744     loadingText: 'Loading...',
31745     /**
31746      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31747      */
31748     resizable: false,
31749     /**
31750      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31751      */
31752     handleHeight : 8,
31753     /**
31754      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31755      * traditional select (defaults to true)
31756      */
31757     editable: true,
31758     /**
31759      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31760      */
31761     allQuery: '',
31762     /**
31763      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31764      */
31765     mode: 'remote',
31766     /**
31767      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31768      * listWidth has a higher value)
31769      */
31770     minListWidth : 70,
31771     /**
31772      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31773      * allow the user to set arbitrary text into the field (defaults to false)
31774      */
31775     forceSelection:false,
31776     /**
31777      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31778      * if typeAhead = true (defaults to 250)
31779      */
31780     typeAheadDelay : 250,
31781     /**
31782      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31783      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31784      */
31785     valueNotFoundText : undefined,
31786     
31787     /**
31788      * @cfg {String} defaultValue The value displayed after loading the store.
31789      */
31790     defaultValue: '',
31791     
31792     /**
31793      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31794      */
31795     blockFocus : false,
31796     
31797     /**
31798      * @cfg {Boolean} disableClear Disable showing of clear button.
31799      */
31800     disableClear : false,
31801     /**
31802      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31803      */
31804     alwaysQuery : false,
31805     
31806     //private
31807     addicon : false,
31808     editicon: false,
31809     
31810     // element that contains real text value.. (when hidden is used..)
31811      
31812     // private
31813     onRender : function(ct, position){
31814         Roo.form.Field.prototype.onRender.call(this, ct, position);
31815         
31816         if(this.store){
31817             this.store.on('beforeload', this.onBeforeLoad, this);
31818             this.store.on('load', this.onLoad, this);
31819             this.store.on('loadexception', this.onLoadException, this);
31820             this.store.load({});
31821         }
31822         
31823         
31824         
31825     },
31826
31827     // private
31828     initEvents : function(){
31829         //Roo.form.ComboBox.superclass.initEvents.call(this);
31830  
31831     },
31832
31833     onDestroy : function(){
31834        
31835         if(this.store){
31836             this.store.un('beforeload', this.onBeforeLoad, this);
31837             this.store.un('load', this.onLoad, this);
31838             this.store.un('loadexception', this.onLoadException, this);
31839         }
31840         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31841     },
31842
31843     // private
31844     fireKey : function(e){
31845         if(e.isNavKeyPress() && !this.list.isVisible()){
31846             this.fireEvent("specialkey", this, e);
31847         }
31848     },
31849
31850     // private
31851     onResize: function(w, h){
31852         
31853         return; 
31854     
31855         
31856     },
31857
31858     /**
31859      * Allow or prevent the user from directly editing the field text.  If false is passed,
31860      * the user will only be able to select from the items defined in the dropdown list.  This method
31861      * is the runtime equivalent of setting the 'editable' config option at config time.
31862      * @param {Boolean} value True to allow the user to directly edit the field text
31863      */
31864     setEditable : function(value){
31865          
31866     },
31867
31868     // private
31869     onBeforeLoad : function(){
31870         
31871         Roo.log("Select before load");
31872         return;
31873     
31874         this.innerList.update(this.loadingText ?
31875                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31876         //this.restrictHeight();
31877         this.selectedIndex = -1;
31878     },
31879
31880     // private
31881     onLoad : function(){
31882
31883     
31884         var dom = this.el.dom;
31885         dom.innerHTML = '';
31886          var od = dom.ownerDocument;
31887          
31888         if (this.emptyText) {
31889             var op = od.createElement('option');
31890             op.setAttribute('value', '');
31891             op.innerHTML = String.format('{0}', this.emptyText);
31892             dom.appendChild(op);
31893         }
31894         if(this.store.getCount() > 0){
31895            
31896             var vf = this.valueField;
31897             var df = this.displayField;
31898             this.store.data.each(function(r) {
31899                 // which colmsn to use... testing - cdoe / title..
31900                 var op = od.createElement('option');
31901                 op.setAttribute('value', r.data[vf]);
31902                 op.innerHTML = String.format('{0}', r.data[df]);
31903                 dom.appendChild(op);
31904             });
31905             if (typeof(this.defaultValue != 'undefined')) {
31906                 this.setValue(this.defaultValue);
31907             }
31908             
31909              
31910         }else{
31911             //this.onEmptyResults();
31912         }
31913         //this.el.focus();
31914     },
31915     // private
31916     onLoadException : function()
31917     {
31918         dom.innerHTML = '';
31919             
31920         Roo.log("Select on load exception");
31921         return;
31922     
31923         this.collapse();
31924         Roo.log(this.store.reader.jsonData);
31925         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31926             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31927         }
31928         
31929         
31930     },
31931     // private
31932     onTypeAhead : function(){
31933          
31934     },
31935
31936     // private
31937     onSelect : function(record, index){
31938         Roo.log('on select?');
31939         return;
31940         if(this.fireEvent('beforeselect', this, record, index) !== false){
31941             this.setFromData(index > -1 ? record.data : false);
31942             this.collapse();
31943             this.fireEvent('select', this, record, index);
31944         }
31945     },
31946
31947     /**
31948      * Returns the currently selected field value or empty string if no value is set.
31949      * @return {String} value The selected value
31950      */
31951     getValue : function(){
31952         var dom = this.el.dom;
31953         this.value = dom.options[dom.selectedIndex].value;
31954         return this.value;
31955         
31956     },
31957
31958     /**
31959      * Clears any text/value currently set in the field
31960      */
31961     clearValue : function(){
31962         this.value = '';
31963         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31964         
31965     },
31966
31967     /**
31968      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31969      * will be displayed in the field.  If the value does not match the data value of an existing item,
31970      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31971      * Otherwise the field will be blank (although the value will still be set).
31972      * @param {String} value The value to match
31973      */
31974     setValue : function(v){
31975         var d = this.el.dom;
31976         for (var i =0; i < d.options.length;i++) {
31977             if (v == d.options[i].value) {
31978                 d.selectedIndex = i;
31979                 this.value = v;
31980                 return;
31981             }
31982         }
31983         this.clearValue();
31984     },
31985     /**
31986      * @property {Object} the last set data for the element
31987      */
31988     
31989     lastData : false,
31990     /**
31991      * Sets the value of the field based on a object which is related to the record format for the store.
31992      * @param {Object} value the value to set as. or false on reset?
31993      */
31994     setFromData : function(o){
31995         Roo.log('setfrom data?');
31996          
31997         
31998         
31999     },
32000     // private
32001     reset : function(){
32002         this.clearValue();
32003     },
32004     // private
32005     findRecord : function(prop, value){
32006         
32007         return false;
32008     
32009         var record;
32010         if(this.store.getCount() > 0){
32011             this.store.each(function(r){
32012                 if(r.data[prop] == value){
32013                     record = r;
32014                     return false;
32015                 }
32016                 return true;
32017             });
32018         }
32019         return record;
32020     },
32021     
32022     getName: function()
32023     {
32024         // returns hidden if it's set..
32025         if (!this.rendered) {return ''};
32026         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32027         
32028     },
32029      
32030
32031     
32032
32033     // private
32034     onEmptyResults : function(){
32035         Roo.log('empty results');
32036         //this.collapse();
32037     },
32038
32039     /**
32040      * Returns true if the dropdown list is expanded, else false.
32041      */
32042     isExpanded : function(){
32043         return false;
32044     },
32045
32046     /**
32047      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32048      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32049      * @param {String} value The data value of the item to select
32050      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32051      * selected item if it is not currently in view (defaults to true)
32052      * @return {Boolean} True if the value matched an item in the list, else false
32053      */
32054     selectByValue : function(v, scrollIntoView){
32055         Roo.log('select By Value');
32056         return false;
32057     
32058         if(v !== undefined && v !== null){
32059             var r = this.findRecord(this.valueField || this.displayField, v);
32060             if(r){
32061                 this.select(this.store.indexOf(r), scrollIntoView);
32062                 return true;
32063             }
32064         }
32065         return false;
32066     },
32067
32068     /**
32069      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32070      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32071      * @param {Number} index The zero-based index of the list item to select
32072      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32073      * selected item if it is not currently in view (defaults to true)
32074      */
32075     select : function(index, scrollIntoView){
32076         Roo.log('select ');
32077         return  ;
32078         
32079         this.selectedIndex = index;
32080         this.view.select(index);
32081         if(scrollIntoView !== false){
32082             var el = this.view.getNode(index);
32083             if(el){
32084                 this.innerList.scrollChildIntoView(el, false);
32085             }
32086         }
32087     },
32088
32089       
32090
32091     // private
32092     validateBlur : function(){
32093         
32094         return;
32095         
32096     },
32097
32098     // private
32099     initQuery : function(){
32100         this.doQuery(this.getRawValue());
32101     },
32102
32103     // private
32104     doForce : function(){
32105         if(this.el.dom.value.length > 0){
32106             this.el.dom.value =
32107                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32108              
32109         }
32110     },
32111
32112     /**
32113      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32114      * query allowing the query action to be canceled if needed.
32115      * @param {String} query The SQL query to execute
32116      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32117      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32118      * saved in the current store (defaults to false)
32119      */
32120     doQuery : function(q, forceAll){
32121         
32122         Roo.log('doQuery?');
32123         if(q === undefined || q === null){
32124             q = '';
32125         }
32126         var qe = {
32127             query: q,
32128             forceAll: forceAll,
32129             combo: this,
32130             cancel:false
32131         };
32132         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32133             return false;
32134         }
32135         q = qe.query;
32136         forceAll = qe.forceAll;
32137         if(forceAll === true || (q.length >= this.minChars)){
32138             if(this.lastQuery != q || this.alwaysQuery){
32139                 this.lastQuery = q;
32140                 if(this.mode == 'local'){
32141                     this.selectedIndex = -1;
32142                     if(forceAll){
32143                         this.store.clearFilter();
32144                     }else{
32145                         this.store.filter(this.displayField, q);
32146                     }
32147                     this.onLoad();
32148                 }else{
32149                     this.store.baseParams[this.queryParam] = q;
32150                     this.store.load({
32151                         params: this.getParams(q)
32152                     });
32153                     this.expand();
32154                 }
32155             }else{
32156                 this.selectedIndex = -1;
32157                 this.onLoad();   
32158             }
32159         }
32160     },
32161
32162     // private
32163     getParams : function(q){
32164         var p = {};
32165         //p[this.queryParam] = q;
32166         if(this.pageSize){
32167             p.start = 0;
32168             p.limit = this.pageSize;
32169         }
32170         return p;
32171     },
32172
32173     /**
32174      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32175      */
32176     collapse : function(){
32177         
32178     },
32179
32180     // private
32181     collapseIf : function(e){
32182         
32183     },
32184
32185     /**
32186      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32187      */
32188     expand : function(){
32189         
32190     } ,
32191
32192     // private
32193      
32194
32195     /** 
32196     * @cfg {Boolean} grow 
32197     * @hide 
32198     */
32199     /** 
32200     * @cfg {Number} growMin 
32201     * @hide 
32202     */
32203     /** 
32204     * @cfg {Number} growMax 
32205     * @hide 
32206     */
32207     /**
32208      * @hide
32209      * @method autoSize
32210      */
32211     
32212     setWidth : function()
32213     {
32214         
32215     },
32216     getResizeEl : function(){
32217         return this.el;
32218     }
32219 });//<script type="text/javasscript">
32220  
32221
32222 /**
32223  * @class Roo.DDView
32224  * A DnD enabled version of Roo.View.
32225  * @param {Element/String} container The Element in which to create the View.
32226  * @param {String} tpl The template string used to create the markup for each element of the View
32227  * @param {Object} config The configuration properties. These include all the config options of
32228  * {@link Roo.View} plus some specific to this class.<br>
32229  * <p>
32230  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32231  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32232  * <p>
32233  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32234 .x-view-drag-insert-above {
32235         border-top:1px dotted #3366cc;
32236 }
32237 .x-view-drag-insert-below {
32238         border-bottom:1px dotted #3366cc;
32239 }
32240 </code></pre>
32241  * 
32242  */
32243  
32244 Roo.DDView = function(container, tpl, config) {
32245     Roo.DDView.superclass.constructor.apply(this, arguments);
32246     this.getEl().setStyle("outline", "0px none");
32247     this.getEl().unselectable();
32248     if (this.dragGroup) {
32249                 this.setDraggable(this.dragGroup.split(","));
32250     }
32251     if (this.dropGroup) {
32252                 this.setDroppable(this.dropGroup.split(","));
32253     }
32254     if (this.deletable) {
32255         this.setDeletable();
32256     }
32257     this.isDirtyFlag = false;
32258         this.addEvents({
32259                 "drop" : true
32260         });
32261 };
32262
32263 Roo.extend(Roo.DDView, Roo.View, {
32264 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32265 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32266 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32267 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32268
32269         isFormField: true,
32270
32271         reset: Roo.emptyFn,
32272         
32273         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32274
32275         validate: function() {
32276                 return true;
32277         },
32278         
32279         destroy: function() {
32280                 this.purgeListeners();
32281                 this.getEl.removeAllListeners();
32282                 this.getEl().remove();
32283                 if (this.dragZone) {
32284                         if (this.dragZone.destroy) {
32285                                 this.dragZone.destroy();
32286                         }
32287                 }
32288                 if (this.dropZone) {
32289                         if (this.dropZone.destroy) {
32290                                 this.dropZone.destroy();
32291                         }
32292                 }
32293         },
32294
32295 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32296         getName: function() {
32297                 return this.name;
32298         },
32299
32300 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32301         setValue: function(v) {
32302                 if (!this.store) {
32303                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32304                 }
32305                 var data = {};
32306                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32307                 this.store.proxy = new Roo.data.MemoryProxy(data);
32308                 this.store.load();
32309         },
32310
32311 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32312         getValue: function() {
32313                 var result = '(';
32314                 this.store.each(function(rec) {
32315                         result += rec.id + ',';
32316                 });
32317                 return result.substr(0, result.length - 1) + ')';
32318         },
32319         
32320         getIds: function() {
32321                 var i = 0, result = new Array(this.store.getCount());
32322                 this.store.each(function(rec) {
32323                         result[i++] = rec.id;
32324                 });
32325                 return result;
32326         },
32327         
32328         isDirty: function() {
32329                 return this.isDirtyFlag;
32330         },
32331
32332 /**
32333  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32334  *      whole Element becomes the target, and this causes the drop gesture to append.
32335  */
32336     getTargetFromEvent : function(e) {
32337                 var target = e.getTarget();
32338                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32339                 target = target.parentNode;
32340                 }
32341                 if (!target) {
32342                         target = this.el.dom.lastChild || this.el.dom;
32343                 }
32344                 return target;
32345     },
32346
32347 /**
32348  *      Create the drag data which consists of an object which has the property "ddel" as
32349  *      the drag proxy element. 
32350  */
32351     getDragData : function(e) {
32352         var target = this.findItemFromChild(e.getTarget());
32353                 if(target) {
32354                         this.handleSelection(e);
32355                         var selNodes = this.getSelectedNodes();
32356             var dragData = {
32357                 source: this,
32358                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32359                 nodes: selNodes,
32360                 records: []
32361                         };
32362                         var selectedIndices = this.getSelectedIndexes();
32363                         for (var i = 0; i < selectedIndices.length; i++) {
32364                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32365                         }
32366                         if (selNodes.length == 1) {
32367                                 dragData.ddel = target.cloneNode(true); // the div element
32368                         } else {
32369                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32370                                 div.className = 'multi-proxy';
32371                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32372                                         div.appendChild(selNodes[i].cloneNode(true));
32373                                 }
32374                                 dragData.ddel = div;
32375                         }
32376             //console.log(dragData)
32377             //console.log(dragData.ddel.innerHTML)
32378                         return dragData;
32379                 }
32380         //console.log('nodragData')
32381                 return false;
32382     },
32383     
32384 /**     Specify to which ddGroup items in this DDView may be dragged. */
32385     setDraggable: function(ddGroup) {
32386         if (ddGroup instanceof Array) {
32387                 Roo.each(ddGroup, this.setDraggable, this);
32388                 return;
32389         }
32390         if (this.dragZone) {
32391                 this.dragZone.addToGroup(ddGroup);
32392         } else {
32393                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32394                                 containerScroll: true,
32395                                 ddGroup: ddGroup 
32396
32397                         });
32398 //                      Draggability implies selection. DragZone's mousedown selects the element.
32399                         if (!this.multiSelect) { this.singleSelect = true; }
32400
32401 //                      Wire the DragZone's handlers up to methods in *this*
32402                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32403                 }
32404     },
32405
32406 /**     Specify from which ddGroup this DDView accepts drops. */
32407     setDroppable: function(ddGroup) {
32408         if (ddGroup instanceof Array) {
32409                 Roo.each(ddGroup, this.setDroppable, this);
32410                 return;
32411         }
32412         if (this.dropZone) {
32413                 this.dropZone.addToGroup(ddGroup);
32414         } else {
32415                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32416                                 containerScroll: true,
32417                                 ddGroup: ddGroup
32418                         });
32419
32420 //                      Wire the DropZone's handlers up to methods in *this*
32421                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32422                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32423                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32424                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32425                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32426                 }
32427     },
32428
32429 /**     Decide whether to drop above or below a View node. */
32430     getDropPoint : function(e, n, dd){
32431         if (n == this.el.dom) { return "above"; }
32432                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32433                 var c = t + (b - t) / 2;
32434                 var y = Roo.lib.Event.getPageY(e);
32435                 if(y <= c) {
32436                         return "above";
32437                 }else{
32438                         return "below";
32439                 }
32440     },
32441
32442     onNodeEnter : function(n, dd, e, data){
32443                 return false;
32444     },
32445     
32446     onNodeOver : function(n, dd, e, data){
32447                 var pt = this.getDropPoint(e, n, dd);
32448                 // set the insert point style on the target node
32449                 var dragElClass = this.dropNotAllowed;
32450                 if (pt) {
32451                         var targetElClass;
32452                         if (pt == "above"){
32453                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32454                                 targetElClass = "x-view-drag-insert-above";
32455                         } else {
32456                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32457                                 targetElClass = "x-view-drag-insert-below";
32458                         }
32459                         if (this.lastInsertClass != targetElClass){
32460                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32461                                 this.lastInsertClass = targetElClass;
32462                         }
32463                 }
32464                 return dragElClass;
32465         },
32466
32467     onNodeOut : function(n, dd, e, data){
32468                 this.removeDropIndicators(n);
32469     },
32470
32471     onNodeDrop : function(n, dd, e, data){
32472         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32473                 return false;
32474         }
32475         var pt = this.getDropPoint(e, n, dd);
32476                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32477                 if (pt == "below") { insertAt++; }
32478                 for (var i = 0; i < data.records.length; i++) {
32479                         var r = data.records[i];
32480                         var dup = this.store.getById(r.id);
32481                         if (dup && (dd != this.dragZone)) {
32482                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32483                         } else {
32484                                 if (data.copy) {
32485                                         this.store.insert(insertAt++, r.copy());
32486                                 } else {
32487                                         data.source.isDirtyFlag = true;
32488                                         r.store.remove(r);
32489                                         this.store.insert(insertAt++, r);
32490                                 }
32491                                 this.isDirtyFlag = true;
32492                         }
32493                 }
32494                 this.dragZone.cachedTarget = null;
32495                 return true;
32496     },
32497
32498     removeDropIndicators : function(n){
32499                 if(n){
32500                         Roo.fly(n).removeClass([
32501                                 "x-view-drag-insert-above",
32502                                 "x-view-drag-insert-below"]);
32503                         this.lastInsertClass = "_noclass";
32504                 }
32505     },
32506
32507 /**
32508  *      Utility method. Add a delete option to the DDView's context menu.
32509  *      @param {String} imageUrl The URL of the "delete" icon image.
32510  */
32511         setDeletable: function(imageUrl) {
32512                 if (!this.singleSelect && !this.multiSelect) {
32513                         this.singleSelect = true;
32514                 }
32515                 var c = this.getContextMenu();
32516                 this.contextMenu.on("itemclick", function(item) {
32517                         switch (item.id) {
32518                                 case "delete":
32519                                         this.remove(this.getSelectedIndexes());
32520                                         break;
32521                         }
32522                 }, this);
32523                 this.contextMenu.add({
32524                         icon: imageUrl,
32525                         id: "delete",
32526                         text: 'Delete'
32527                 });
32528         },
32529         
32530 /**     Return the context menu for this DDView. */
32531         getContextMenu: function() {
32532                 if (!this.contextMenu) {
32533 //                      Create the View's context menu
32534                         this.contextMenu = new Roo.menu.Menu({
32535                                 id: this.id + "-contextmenu"
32536                         });
32537                         this.el.on("contextmenu", this.showContextMenu, this);
32538                 }
32539                 return this.contextMenu;
32540         },
32541         
32542         disableContextMenu: function() {
32543                 if (this.contextMenu) {
32544                         this.el.un("contextmenu", this.showContextMenu, this);
32545                 }
32546         },
32547
32548         showContextMenu: function(e, item) {
32549         item = this.findItemFromChild(e.getTarget());
32550                 if (item) {
32551                         e.stopEvent();
32552                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32553                         this.contextMenu.showAt(e.getXY());
32554             }
32555     },
32556
32557 /**
32558  *      Remove {@link Roo.data.Record}s at the specified indices.
32559  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32560  */
32561     remove: function(selectedIndices) {
32562                 selectedIndices = [].concat(selectedIndices);
32563                 for (var i = 0; i < selectedIndices.length; i++) {
32564                         var rec = this.store.getAt(selectedIndices[i]);
32565                         this.store.remove(rec);
32566                 }
32567     },
32568
32569 /**
32570  *      Double click fires the event, but also, if this is draggable, and there is only one other
32571  *      related DropZone, it transfers the selected node.
32572  */
32573     onDblClick : function(e){
32574         var item = this.findItemFromChild(e.getTarget());
32575         if(item){
32576             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32577                 return false;
32578             }
32579             if (this.dragGroup) {
32580                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32581                     while (targets.indexOf(this.dropZone) > -1) {
32582                             targets.remove(this.dropZone);
32583                                 }
32584                     if (targets.length == 1) {
32585                                         this.dragZone.cachedTarget = null;
32586                         var el = Roo.get(targets[0].getEl());
32587                         var box = el.getBox(true);
32588                         targets[0].onNodeDrop(el.dom, {
32589                                 target: el.dom,
32590                                 xy: [box.x, box.y + box.height - 1]
32591                         }, null, this.getDragData(e));
32592                     }
32593                 }
32594         }
32595     },
32596     
32597     handleSelection: function(e) {
32598                 this.dragZone.cachedTarget = null;
32599         var item = this.findItemFromChild(e.getTarget());
32600         if (!item) {
32601                 this.clearSelections(true);
32602                 return;
32603         }
32604                 if (item && (this.multiSelect || this.singleSelect)){
32605                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32606                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32607                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32608                                 this.unselect(item);
32609                         } else {
32610                                 this.select(item, this.multiSelect && e.ctrlKey);
32611                                 this.lastSelection = item;
32612                         }
32613                 }
32614     },
32615
32616     onItemClick : function(item, index, e){
32617                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32618                         return false;
32619                 }
32620                 return true;
32621     },
32622
32623     unselect : function(nodeInfo, suppressEvent){
32624                 var node = this.getNode(nodeInfo);
32625                 if(node && this.isSelected(node)){
32626                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32627                                 Roo.fly(node).removeClass(this.selectedClass);
32628                                 this.selections.remove(node);
32629                                 if(!suppressEvent){
32630                                         this.fireEvent("selectionchange", this, this.selections);
32631                                 }
32632                         }
32633                 }
32634     }
32635 });
32636 /*
32637  * Based on:
32638  * Ext JS Library 1.1.1
32639  * Copyright(c) 2006-2007, Ext JS, LLC.
32640  *
32641  * Originally Released Under LGPL - original licence link has changed is not relivant.
32642  *
32643  * Fork - LGPL
32644  * <script type="text/javascript">
32645  */
32646  
32647 /**
32648  * @class Roo.LayoutManager
32649  * @extends Roo.util.Observable
32650  * Base class for layout managers.
32651  */
32652 Roo.LayoutManager = function(container, config){
32653     Roo.LayoutManager.superclass.constructor.call(this);
32654     this.el = Roo.get(container);
32655     // ie scrollbar fix
32656     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32657         document.body.scroll = "no";
32658     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32659         this.el.position('relative');
32660     }
32661     this.id = this.el.id;
32662     this.el.addClass("x-layout-container");
32663     /** false to disable window resize monitoring @type Boolean */
32664     this.monitorWindowResize = true;
32665     this.regions = {};
32666     this.addEvents({
32667         /**
32668          * @event layout
32669          * Fires when a layout is performed. 
32670          * @param {Roo.LayoutManager} this
32671          */
32672         "layout" : true,
32673         /**
32674          * @event regionresized
32675          * Fires when the user resizes a region. 
32676          * @param {Roo.LayoutRegion} region The resized region
32677          * @param {Number} newSize The new size (width for east/west, height for north/south)
32678          */
32679         "regionresized" : true,
32680         /**
32681          * @event regioncollapsed
32682          * Fires when a region is collapsed. 
32683          * @param {Roo.LayoutRegion} region The collapsed region
32684          */
32685         "regioncollapsed" : true,
32686         /**
32687          * @event regionexpanded
32688          * Fires when a region is expanded.  
32689          * @param {Roo.LayoutRegion} region The expanded region
32690          */
32691         "regionexpanded" : true
32692     });
32693     this.updating = false;
32694     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32695 };
32696
32697 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32698     /**
32699      * Returns true if this layout is currently being updated
32700      * @return {Boolean}
32701      */
32702     isUpdating : function(){
32703         return this.updating; 
32704     },
32705     
32706     /**
32707      * Suspend the LayoutManager from doing auto-layouts while
32708      * making multiple add or remove calls
32709      */
32710     beginUpdate : function(){
32711         this.updating = true;    
32712     },
32713     
32714     /**
32715      * Restore auto-layouts and optionally disable the manager from performing a layout
32716      * @param {Boolean} noLayout true to disable a layout update 
32717      */
32718     endUpdate : function(noLayout){
32719         this.updating = false;
32720         if(!noLayout){
32721             this.layout();
32722         }    
32723     },
32724     
32725     layout: function(){
32726         
32727     },
32728     
32729     onRegionResized : function(region, newSize){
32730         this.fireEvent("regionresized", region, newSize);
32731         this.layout();
32732     },
32733     
32734     onRegionCollapsed : function(region){
32735         this.fireEvent("regioncollapsed", region);
32736     },
32737     
32738     onRegionExpanded : function(region){
32739         this.fireEvent("regionexpanded", region);
32740     },
32741         
32742     /**
32743      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32744      * performs box-model adjustments.
32745      * @return {Object} The size as an object {width: (the width), height: (the height)}
32746      */
32747     getViewSize : function(){
32748         var size;
32749         if(this.el.dom != document.body){
32750             size = this.el.getSize();
32751         }else{
32752             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32753         }
32754         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32755         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32756         return size;
32757     },
32758     
32759     /**
32760      * Returns the Element this layout is bound to.
32761      * @return {Roo.Element}
32762      */
32763     getEl : function(){
32764         return this.el;
32765     },
32766     
32767     /**
32768      * Returns the specified region.
32769      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32770      * @return {Roo.LayoutRegion}
32771      */
32772     getRegion : function(target){
32773         return this.regions[target.toLowerCase()];
32774     },
32775     
32776     onWindowResize : function(){
32777         if(this.monitorWindowResize){
32778             this.layout();
32779         }
32780     }
32781 });/*
32782  * Based on:
32783  * Ext JS Library 1.1.1
32784  * Copyright(c) 2006-2007, Ext JS, LLC.
32785  *
32786  * Originally Released Under LGPL - original licence link has changed is not relivant.
32787  *
32788  * Fork - LGPL
32789  * <script type="text/javascript">
32790  */
32791 /**
32792  * @class Roo.BorderLayout
32793  * @extends Roo.LayoutManager
32794  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32795  * please see: <br><br>
32796  * <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>
32797  * <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>
32798  * Example:
32799  <pre><code>
32800  var layout = new Roo.BorderLayout(document.body, {
32801     north: {
32802         initialSize: 25,
32803         titlebar: false
32804     },
32805     west: {
32806         split:true,
32807         initialSize: 200,
32808         minSize: 175,
32809         maxSize: 400,
32810         titlebar: true,
32811         collapsible: true
32812     },
32813     east: {
32814         split:true,
32815         initialSize: 202,
32816         minSize: 175,
32817         maxSize: 400,
32818         titlebar: true,
32819         collapsible: true
32820     },
32821     south: {
32822         split:true,
32823         initialSize: 100,
32824         minSize: 100,
32825         maxSize: 200,
32826         titlebar: true,
32827         collapsible: true
32828     },
32829     center: {
32830         titlebar: true,
32831         autoScroll:true,
32832         resizeTabs: true,
32833         minTabWidth: 50,
32834         preferredTabWidth: 150
32835     }
32836 });
32837
32838 // shorthand
32839 var CP = Roo.ContentPanel;
32840
32841 layout.beginUpdate();
32842 layout.add("north", new CP("north", "North"));
32843 layout.add("south", new CP("south", {title: "South", closable: true}));
32844 layout.add("west", new CP("west", {title: "West"}));
32845 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32846 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32847 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32848 layout.getRegion("center").showPanel("center1");
32849 layout.endUpdate();
32850 </code></pre>
32851
32852 <b>The container the layout is rendered into can be either the body element or any other element.
32853 If it is not the body element, the container needs to either be an absolute positioned element,
32854 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32855 the container size if it is not the body element.</b>
32856
32857 * @constructor
32858 * Create a new BorderLayout
32859 * @param {String/HTMLElement/Element} container The container this layout is bound to
32860 * @param {Object} config Configuration options
32861  */
32862 Roo.BorderLayout = function(container, config){
32863     config = config || {};
32864     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32865     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32866     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32867         var target = this.factory.validRegions[i];
32868         if(config[target]){
32869             this.addRegion(target, config[target]);
32870         }
32871     }
32872 };
32873
32874 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32875     /**
32876      * Creates and adds a new region if it doesn't already exist.
32877      * @param {String} target The target region key (north, south, east, west or center).
32878      * @param {Object} config The regions config object
32879      * @return {BorderLayoutRegion} The new region
32880      */
32881     addRegion : function(target, config){
32882         if(!this.regions[target]){
32883             var r = this.factory.create(target, this, config);
32884             this.bindRegion(target, r);
32885         }
32886         return this.regions[target];
32887     },
32888
32889     // private (kinda)
32890     bindRegion : function(name, r){
32891         this.regions[name] = r;
32892         r.on("visibilitychange", this.layout, this);
32893         r.on("paneladded", this.layout, this);
32894         r.on("panelremoved", this.layout, this);
32895         r.on("invalidated", this.layout, this);
32896         r.on("resized", this.onRegionResized, this);
32897         r.on("collapsed", this.onRegionCollapsed, this);
32898         r.on("expanded", this.onRegionExpanded, this);
32899     },
32900
32901     /**
32902      * Performs a layout update.
32903      */
32904     layout : function(){
32905         if(this.updating) return;
32906         var size = this.getViewSize();
32907         var w = size.width;
32908         var h = size.height;
32909         var centerW = w;
32910         var centerH = h;
32911         var centerY = 0;
32912         var centerX = 0;
32913         //var x = 0, y = 0;
32914
32915         var rs = this.regions;
32916         var north = rs["north"];
32917         var south = rs["south"]; 
32918         var west = rs["west"];
32919         var east = rs["east"];
32920         var center = rs["center"];
32921         //if(this.hideOnLayout){ // not supported anymore
32922             //c.el.setStyle("display", "none");
32923         //}
32924         if(north && north.isVisible()){
32925             var b = north.getBox();
32926             var m = north.getMargins();
32927             b.width = w - (m.left+m.right);
32928             b.x = m.left;
32929             b.y = m.top;
32930             centerY = b.height + b.y + m.bottom;
32931             centerH -= centerY;
32932             north.updateBox(this.safeBox(b));
32933         }
32934         if(south && south.isVisible()){
32935             var b = south.getBox();
32936             var m = south.getMargins();
32937             b.width = w - (m.left+m.right);
32938             b.x = m.left;
32939             var totalHeight = (b.height + m.top + m.bottom);
32940             b.y = h - totalHeight + m.top;
32941             centerH -= totalHeight;
32942             south.updateBox(this.safeBox(b));
32943         }
32944         if(west && west.isVisible()){
32945             var b = west.getBox();
32946             var m = west.getMargins();
32947             b.height = centerH - (m.top+m.bottom);
32948             b.x = m.left;
32949             b.y = centerY + m.top;
32950             var totalWidth = (b.width + m.left + m.right);
32951             centerX += totalWidth;
32952             centerW -= totalWidth;
32953             west.updateBox(this.safeBox(b));
32954         }
32955         if(east && east.isVisible()){
32956             var b = east.getBox();
32957             var m = east.getMargins();
32958             b.height = centerH - (m.top+m.bottom);
32959             var totalWidth = (b.width + m.left + m.right);
32960             b.x = w - totalWidth + m.left;
32961             b.y = centerY + m.top;
32962             centerW -= totalWidth;
32963             east.updateBox(this.safeBox(b));
32964         }
32965         if(center){
32966             var m = center.getMargins();
32967             var centerBox = {
32968                 x: centerX + m.left,
32969                 y: centerY + m.top,
32970                 width: centerW - (m.left+m.right),
32971                 height: centerH - (m.top+m.bottom)
32972             };
32973             //if(this.hideOnLayout){
32974                 //center.el.setStyle("display", "block");
32975             //}
32976             center.updateBox(this.safeBox(centerBox));
32977         }
32978         this.el.repaint();
32979         this.fireEvent("layout", this);
32980     },
32981
32982     // private
32983     safeBox : function(box){
32984         box.width = Math.max(0, box.width);
32985         box.height = Math.max(0, box.height);
32986         return box;
32987     },
32988
32989     /**
32990      * Adds a ContentPanel (or subclass) to this layout.
32991      * @param {String} target The target region key (north, south, east, west or center).
32992      * @param {Roo.ContentPanel} panel The panel to add
32993      * @return {Roo.ContentPanel} The added panel
32994      */
32995     add : function(target, panel){
32996          
32997         target = target.toLowerCase();
32998         return this.regions[target].add(panel);
32999     },
33000
33001     /**
33002      * Remove a ContentPanel (or subclass) to this layout.
33003      * @param {String} target The target region key (north, south, east, west or center).
33004      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33005      * @return {Roo.ContentPanel} The removed panel
33006      */
33007     remove : function(target, panel){
33008         target = target.toLowerCase();
33009         return this.regions[target].remove(panel);
33010     },
33011
33012     /**
33013      * Searches all regions for a panel with the specified id
33014      * @param {String} panelId
33015      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33016      */
33017     findPanel : function(panelId){
33018         var rs = this.regions;
33019         for(var target in rs){
33020             if(typeof rs[target] != "function"){
33021                 var p = rs[target].getPanel(panelId);
33022                 if(p){
33023                     return p;
33024                 }
33025             }
33026         }
33027         return null;
33028     },
33029
33030     /**
33031      * Searches all regions for a panel with the specified id and activates (shows) it.
33032      * @param {String/ContentPanel} panelId The panels id or the panel itself
33033      * @return {Roo.ContentPanel} The shown panel or null
33034      */
33035     showPanel : function(panelId) {
33036       var rs = this.regions;
33037       for(var target in rs){
33038          var r = rs[target];
33039          if(typeof r != "function"){
33040             if(r.hasPanel(panelId)){
33041                return r.showPanel(panelId);
33042             }
33043          }
33044       }
33045       return null;
33046    },
33047
33048    /**
33049      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33050      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33051      */
33052     restoreState : function(provider){
33053         if(!provider){
33054             provider = Roo.state.Manager;
33055         }
33056         var sm = new Roo.LayoutStateManager();
33057         sm.init(this, provider);
33058     },
33059
33060     /**
33061      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33062      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33063      * a valid ContentPanel config object.  Example:
33064      * <pre><code>
33065 // Create the main layout
33066 var layout = new Roo.BorderLayout('main-ct', {
33067     west: {
33068         split:true,
33069         minSize: 175,
33070         titlebar: true
33071     },
33072     center: {
33073         title:'Components'
33074     }
33075 }, 'main-ct');
33076
33077 // Create and add multiple ContentPanels at once via configs
33078 layout.batchAdd({
33079    west: {
33080        id: 'source-files',
33081        autoCreate:true,
33082        title:'Ext Source Files',
33083        autoScroll:true,
33084        fitToFrame:true
33085    },
33086    center : {
33087        el: cview,
33088        autoScroll:true,
33089        fitToFrame:true,
33090        toolbar: tb,
33091        resizeEl:'cbody'
33092    }
33093 });
33094 </code></pre>
33095      * @param {Object} regions An object containing ContentPanel configs by region name
33096      */
33097     batchAdd : function(regions){
33098         this.beginUpdate();
33099         for(var rname in regions){
33100             var lr = this.regions[rname];
33101             if(lr){
33102                 this.addTypedPanels(lr, regions[rname]);
33103             }
33104         }
33105         this.endUpdate();
33106     },
33107
33108     // private
33109     addTypedPanels : function(lr, ps){
33110         if(typeof ps == 'string'){
33111             lr.add(new Roo.ContentPanel(ps));
33112         }
33113         else if(ps instanceof Array){
33114             for(var i =0, len = ps.length; i < len; i++){
33115                 this.addTypedPanels(lr, ps[i]);
33116             }
33117         }
33118         else if(!ps.events){ // raw config?
33119             var el = ps.el;
33120             delete ps.el; // prevent conflict
33121             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33122         }
33123         else {  // panel object assumed!
33124             lr.add(ps);
33125         }
33126     },
33127     /**
33128      * Adds a xtype elements to the layout.
33129      * <pre><code>
33130
33131 layout.addxtype({
33132        xtype : 'ContentPanel',
33133        region: 'west',
33134        items: [ .... ]
33135    }
33136 );
33137
33138 layout.addxtype({
33139         xtype : 'NestedLayoutPanel',
33140         region: 'west',
33141         layout: {
33142            center: { },
33143            west: { }   
33144         },
33145         items : [ ... list of content panels or nested layout panels.. ]
33146    }
33147 );
33148 </code></pre>
33149      * @param {Object} cfg Xtype definition of item to add.
33150      */
33151     addxtype : function(cfg)
33152     {
33153         // basically accepts a pannel...
33154         // can accept a layout region..!?!?
33155         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33156         
33157         if (!cfg.xtype.match(/Panel$/)) {
33158             return false;
33159         }
33160         var ret = false;
33161         
33162         if (typeof(cfg.region) == 'undefined') {
33163             Roo.log("Failed to add Panel, region was not set");
33164             Roo.log(cfg);
33165             return false;
33166         }
33167         var region = cfg.region;
33168         delete cfg.region;
33169         
33170           
33171         var xitems = [];
33172         if (cfg.items) {
33173             xitems = cfg.items;
33174             delete cfg.items;
33175         }
33176         var nb = false;
33177         
33178         switch(cfg.xtype) 
33179         {
33180             case 'ContentPanel':  // ContentPanel (el, cfg)
33181             case 'ScrollPanel':  // ContentPanel (el, cfg)
33182             case 'ViewPanel': 
33183                 if(cfg.autoCreate) {
33184                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33185                 } else {
33186                     var el = this.el.createChild();
33187                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33188                 }
33189                 
33190                 this.add(region, ret);
33191                 break;
33192             
33193             
33194             case 'TreePanel': // our new panel!
33195                 cfg.el = this.el.createChild();
33196                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33197                 this.add(region, ret);
33198                 break;
33199             
33200             case 'NestedLayoutPanel': 
33201                 // create a new Layout (which is  a Border Layout...
33202                 var el = this.el.createChild();
33203                 var clayout = cfg.layout;
33204                 delete cfg.layout;
33205                 clayout.items   = clayout.items  || [];
33206                 // replace this exitems with the clayout ones..
33207                 xitems = clayout.items;
33208                  
33209                 
33210                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33211                     cfg.background = false;
33212                 }
33213                 var layout = new Roo.BorderLayout(el, clayout);
33214                 
33215                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33216                 //console.log('adding nested layout panel '  + cfg.toSource());
33217                 this.add(region, ret);
33218                 nb = {}; /// find first...
33219                 break;
33220                 
33221             case 'GridPanel': 
33222             
33223                 // needs grid and region
33224                 
33225                 //var el = this.getRegion(region).el.createChild();
33226                 var el = this.el.createChild();
33227                 // create the grid first...
33228                 
33229                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33230                 delete cfg.grid;
33231                 if (region == 'center' && this.active ) {
33232                     cfg.background = false;
33233                 }
33234                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33235                 
33236                 this.add(region, ret);
33237                 if (cfg.background) {
33238                     ret.on('activate', function(gp) {
33239                         if (!gp.grid.rendered) {
33240                             gp.grid.render();
33241                         }
33242                     });
33243                 } else {
33244                     grid.render();
33245                 }
33246                 break;
33247            
33248            
33249            
33250                 
33251                 
33252                 
33253             default:
33254                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33255                     
33256                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33257                     this.add(region, ret);
33258                 } else {
33259                 
33260                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33261                     return null;
33262                 }
33263                 
33264              // GridPanel (grid, cfg)
33265             
33266         }
33267         this.beginUpdate();
33268         // add children..
33269         var region = '';
33270         var abn = {};
33271         Roo.each(xitems, function(i)  {
33272             region = nb && i.region ? i.region : false;
33273             
33274             var add = ret.addxtype(i);
33275            
33276             if (region) {
33277                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33278                 if (!i.background) {
33279                     abn[region] = nb[region] ;
33280                 }
33281             }
33282             
33283         });
33284         this.endUpdate();
33285
33286         // make the last non-background panel active..
33287         //if (nb) { Roo.log(abn); }
33288         if (nb) {
33289             
33290             for(var r in abn) {
33291                 region = this.getRegion(r);
33292                 if (region) {
33293                     // tried using nb[r], but it does not work..
33294                      
33295                     region.showPanel(abn[r]);
33296                    
33297                 }
33298             }
33299         }
33300         return ret;
33301         
33302     }
33303 });
33304
33305 /**
33306  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33307  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33308  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33309  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33310  * <pre><code>
33311 // shorthand
33312 var CP = Roo.ContentPanel;
33313
33314 var layout = Roo.BorderLayout.create({
33315     north: {
33316         initialSize: 25,
33317         titlebar: false,
33318         panels: [new CP("north", "North")]
33319     },
33320     west: {
33321         split:true,
33322         initialSize: 200,
33323         minSize: 175,
33324         maxSize: 400,
33325         titlebar: true,
33326         collapsible: true,
33327         panels: [new CP("west", {title: "West"})]
33328     },
33329     east: {
33330         split:true,
33331         initialSize: 202,
33332         minSize: 175,
33333         maxSize: 400,
33334         titlebar: true,
33335         collapsible: true,
33336         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33337     },
33338     south: {
33339         split:true,
33340         initialSize: 100,
33341         minSize: 100,
33342         maxSize: 200,
33343         titlebar: true,
33344         collapsible: true,
33345         panels: [new CP("south", {title: "South", closable: true})]
33346     },
33347     center: {
33348         titlebar: true,
33349         autoScroll:true,
33350         resizeTabs: true,
33351         minTabWidth: 50,
33352         preferredTabWidth: 150,
33353         panels: [
33354             new CP("center1", {title: "Close Me", closable: true}),
33355             new CP("center2", {title: "Center Panel", closable: false})
33356         ]
33357     }
33358 }, document.body);
33359
33360 layout.getRegion("center").showPanel("center1");
33361 </code></pre>
33362  * @param config
33363  * @param targetEl
33364  */
33365 Roo.BorderLayout.create = function(config, targetEl){
33366     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33367     layout.beginUpdate();
33368     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33369     for(var j = 0, jlen = regions.length; j < jlen; j++){
33370         var lr = regions[j];
33371         if(layout.regions[lr] && config[lr].panels){
33372             var r = layout.regions[lr];
33373             var ps = config[lr].panels;
33374             layout.addTypedPanels(r, ps);
33375         }
33376     }
33377     layout.endUpdate();
33378     return layout;
33379 };
33380
33381 // private
33382 Roo.BorderLayout.RegionFactory = {
33383     // private
33384     validRegions : ["north","south","east","west","center"],
33385
33386     // private
33387     create : function(target, mgr, config){
33388         target = target.toLowerCase();
33389         if(config.lightweight || config.basic){
33390             return new Roo.BasicLayoutRegion(mgr, config, target);
33391         }
33392         switch(target){
33393             case "north":
33394                 return new Roo.NorthLayoutRegion(mgr, config);
33395             case "south":
33396                 return new Roo.SouthLayoutRegion(mgr, config);
33397             case "east":
33398                 return new Roo.EastLayoutRegion(mgr, config);
33399             case "west":
33400                 return new Roo.WestLayoutRegion(mgr, config);
33401             case "center":
33402                 return new Roo.CenterLayoutRegion(mgr, config);
33403         }
33404         throw 'Layout region "'+target+'" not supported.';
33405     }
33406 };/*
33407  * Based on:
33408  * Ext JS Library 1.1.1
33409  * Copyright(c) 2006-2007, Ext JS, LLC.
33410  *
33411  * Originally Released Under LGPL - original licence link has changed is not relivant.
33412  *
33413  * Fork - LGPL
33414  * <script type="text/javascript">
33415  */
33416  
33417 /**
33418  * @class Roo.BasicLayoutRegion
33419  * @extends Roo.util.Observable
33420  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33421  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33422  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33423  */
33424 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33425     this.mgr = mgr;
33426     this.position  = pos;
33427     this.events = {
33428         /**
33429          * @scope Roo.BasicLayoutRegion
33430          */
33431         
33432         /**
33433          * @event beforeremove
33434          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33435          * @param {Roo.LayoutRegion} this
33436          * @param {Roo.ContentPanel} panel The panel
33437          * @param {Object} e The cancel event object
33438          */
33439         "beforeremove" : true,
33440         /**
33441          * @event invalidated
33442          * Fires when the layout for this region is changed.
33443          * @param {Roo.LayoutRegion} this
33444          */
33445         "invalidated" : true,
33446         /**
33447          * @event visibilitychange
33448          * Fires when this region is shown or hidden 
33449          * @param {Roo.LayoutRegion} this
33450          * @param {Boolean} visibility true or false
33451          */
33452         "visibilitychange" : true,
33453         /**
33454          * @event paneladded
33455          * Fires when a panel is added. 
33456          * @param {Roo.LayoutRegion} this
33457          * @param {Roo.ContentPanel} panel The panel
33458          */
33459         "paneladded" : true,
33460         /**
33461          * @event panelremoved
33462          * Fires when a panel is removed. 
33463          * @param {Roo.LayoutRegion} this
33464          * @param {Roo.ContentPanel} panel The panel
33465          */
33466         "panelremoved" : true,
33467         /**
33468          * @event collapsed
33469          * Fires when this region is collapsed.
33470          * @param {Roo.LayoutRegion} this
33471          */
33472         "collapsed" : true,
33473         /**
33474          * @event expanded
33475          * Fires when this region is expanded.
33476          * @param {Roo.LayoutRegion} this
33477          */
33478         "expanded" : true,
33479         /**
33480          * @event slideshow
33481          * Fires when this region is slid into view.
33482          * @param {Roo.LayoutRegion} this
33483          */
33484         "slideshow" : true,
33485         /**
33486          * @event slidehide
33487          * Fires when this region slides out of view. 
33488          * @param {Roo.LayoutRegion} this
33489          */
33490         "slidehide" : true,
33491         /**
33492          * @event panelactivated
33493          * Fires when a panel is activated. 
33494          * @param {Roo.LayoutRegion} this
33495          * @param {Roo.ContentPanel} panel The activated panel
33496          */
33497         "panelactivated" : true,
33498         /**
33499          * @event resized
33500          * Fires when the user resizes this region. 
33501          * @param {Roo.LayoutRegion} this
33502          * @param {Number} newSize The new size (width for east/west, height for north/south)
33503          */
33504         "resized" : true
33505     };
33506     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33507     this.panels = new Roo.util.MixedCollection();
33508     this.panels.getKey = this.getPanelId.createDelegate(this);
33509     this.box = null;
33510     this.activePanel = null;
33511     // ensure listeners are added...
33512     
33513     if (config.listeners || config.events) {
33514         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33515             listeners : config.listeners || {},
33516             events : config.events || {}
33517         });
33518     }
33519     
33520     if(skipConfig !== true){
33521         this.applyConfig(config);
33522     }
33523 };
33524
33525 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33526     getPanelId : function(p){
33527         return p.getId();
33528     },
33529     
33530     applyConfig : function(config){
33531         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33532         this.config = config;
33533         
33534     },
33535     
33536     /**
33537      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33538      * the width, for horizontal (north, south) the height.
33539      * @param {Number} newSize The new width or height
33540      */
33541     resizeTo : function(newSize){
33542         var el = this.el ? this.el :
33543                  (this.activePanel ? this.activePanel.getEl() : null);
33544         if(el){
33545             switch(this.position){
33546                 case "east":
33547                 case "west":
33548                     el.setWidth(newSize);
33549                     this.fireEvent("resized", this, newSize);
33550                 break;
33551                 case "north":
33552                 case "south":
33553                     el.setHeight(newSize);
33554                     this.fireEvent("resized", this, newSize);
33555                 break;                
33556             }
33557         }
33558     },
33559     
33560     getBox : function(){
33561         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33562     },
33563     
33564     getMargins : function(){
33565         return this.margins;
33566     },
33567     
33568     updateBox : function(box){
33569         this.box = box;
33570         var el = this.activePanel.getEl();
33571         el.dom.style.left = box.x + "px";
33572         el.dom.style.top = box.y + "px";
33573         this.activePanel.setSize(box.width, box.height);
33574     },
33575     
33576     /**
33577      * Returns the container element for this region.
33578      * @return {Roo.Element}
33579      */
33580     getEl : function(){
33581         return this.activePanel;
33582     },
33583     
33584     /**
33585      * Returns true if this region is currently visible.
33586      * @return {Boolean}
33587      */
33588     isVisible : function(){
33589         return this.activePanel ? true : false;
33590     },
33591     
33592     setActivePanel : function(panel){
33593         panel = this.getPanel(panel);
33594         if(this.activePanel && this.activePanel != panel){
33595             this.activePanel.setActiveState(false);
33596             this.activePanel.getEl().setLeftTop(-10000,-10000);
33597         }
33598         this.activePanel = panel;
33599         panel.setActiveState(true);
33600         if(this.box){
33601             panel.setSize(this.box.width, this.box.height);
33602         }
33603         this.fireEvent("panelactivated", this, panel);
33604         this.fireEvent("invalidated");
33605     },
33606     
33607     /**
33608      * Show the specified panel.
33609      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33610      * @return {Roo.ContentPanel} The shown panel or null
33611      */
33612     showPanel : function(panel){
33613         if(panel = this.getPanel(panel)){
33614             this.setActivePanel(panel);
33615         }
33616         return panel;
33617     },
33618     
33619     /**
33620      * Get the active panel for this region.
33621      * @return {Roo.ContentPanel} The active panel or null
33622      */
33623     getActivePanel : function(){
33624         return this.activePanel;
33625     },
33626     
33627     /**
33628      * Add the passed ContentPanel(s)
33629      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33630      * @return {Roo.ContentPanel} The panel added (if only one was added)
33631      */
33632     add : function(panel){
33633         if(arguments.length > 1){
33634             for(var i = 0, len = arguments.length; i < len; i++) {
33635                 this.add(arguments[i]);
33636             }
33637             return null;
33638         }
33639         if(this.hasPanel(panel)){
33640             this.showPanel(panel);
33641             return panel;
33642         }
33643         var el = panel.getEl();
33644         if(el.dom.parentNode != this.mgr.el.dom){
33645             this.mgr.el.dom.appendChild(el.dom);
33646         }
33647         if(panel.setRegion){
33648             panel.setRegion(this);
33649         }
33650         this.panels.add(panel);
33651         el.setStyle("position", "absolute");
33652         if(!panel.background){
33653             this.setActivePanel(panel);
33654             if(this.config.initialSize && this.panels.getCount()==1){
33655                 this.resizeTo(this.config.initialSize);
33656             }
33657         }
33658         this.fireEvent("paneladded", this, panel);
33659         return panel;
33660     },
33661     
33662     /**
33663      * Returns true if the panel is in this region.
33664      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33665      * @return {Boolean}
33666      */
33667     hasPanel : function(panel){
33668         if(typeof panel == "object"){ // must be panel obj
33669             panel = panel.getId();
33670         }
33671         return this.getPanel(panel) ? true : false;
33672     },
33673     
33674     /**
33675      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33676      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33677      * @param {Boolean} preservePanel Overrides the config preservePanel option
33678      * @return {Roo.ContentPanel} The panel that was removed
33679      */
33680     remove : function(panel, preservePanel){
33681         panel = this.getPanel(panel);
33682         if(!panel){
33683             return null;
33684         }
33685         var e = {};
33686         this.fireEvent("beforeremove", this, panel, e);
33687         if(e.cancel === true){
33688             return null;
33689         }
33690         var panelId = panel.getId();
33691         this.panels.removeKey(panelId);
33692         return panel;
33693     },
33694     
33695     /**
33696      * Returns the panel specified or null if it's not in this region.
33697      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33698      * @return {Roo.ContentPanel}
33699      */
33700     getPanel : function(id){
33701         if(typeof id == "object"){ // must be panel obj
33702             return id;
33703         }
33704         return this.panels.get(id);
33705     },
33706     
33707     /**
33708      * Returns this regions position (north/south/east/west/center).
33709      * @return {String} 
33710      */
33711     getPosition: function(){
33712         return this.position;    
33713     }
33714 });/*
33715  * Based on:
33716  * Ext JS Library 1.1.1
33717  * Copyright(c) 2006-2007, Ext JS, LLC.
33718  *
33719  * Originally Released Under LGPL - original licence link has changed is not relivant.
33720  *
33721  * Fork - LGPL
33722  * <script type="text/javascript">
33723  */
33724  
33725 /**
33726  * @class Roo.LayoutRegion
33727  * @extends Roo.BasicLayoutRegion
33728  * This class represents a region in a layout manager.
33729  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33730  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33731  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33732  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33733  * @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})
33734  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33735  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33736  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33737  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33738  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33739  * @cfg {String}    title           The title for the region (overrides panel titles)
33740  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33741  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33742  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33743  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33744  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33745  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33746  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33747  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33748  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33749  * @cfg {Boolean}   showPin         True to show a pin button
33750  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33751  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33752  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33753  * @cfg {Number}    width           For East/West panels
33754  * @cfg {Number}    height          For North/South panels
33755  * @cfg {Boolean}   split           To show the splitter
33756  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33757  */
33758 Roo.LayoutRegion = function(mgr, config, pos){
33759     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33760     var dh = Roo.DomHelper;
33761     /** This region's container element 
33762     * @type Roo.Element */
33763     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33764     /** This region's title element 
33765     * @type Roo.Element */
33766
33767     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33768         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33769         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33770     ]}, true);
33771     this.titleEl.enableDisplayMode();
33772     /** This region's title text element 
33773     * @type HTMLElement */
33774     this.titleTextEl = this.titleEl.dom.firstChild;
33775     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33776     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33777     this.closeBtn.enableDisplayMode();
33778     this.closeBtn.on("click", this.closeClicked, this);
33779     this.closeBtn.hide();
33780
33781     this.createBody(config);
33782     this.visible = true;
33783     this.collapsed = false;
33784
33785     if(config.hideWhenEmpty){
33786         this.hide();
33787         this.on("paneladded", this.validateVisibility, this);
33788         this.on("panelremoved", this.validateVisibility, this);
33789     }
33790     this.applyConfig(config);
33791 };
33792
33793 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33794
33795     createBody : function(){
33796         /** This region's body element 
33797         * @type Roo.Element */
33798         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33799     },
33800
33801     applyConfig : function(c){
33802         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33803             var dh = Roo.DomHelper;
33804             if(c.titlebar !== false){
33805                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33806                 this.collapseBtn.on("click", this.collapse, this);
33807                 this.collapseBtn.enableDisplayMode();
33808
33809                 if(c.showPin === true || this.showPin){
33810                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33811                     this.stickBtn.enableDisplayMode();
33812                     this.stickBtn.on("click", this.expand, this);
33813                     this.stickBtn.hide();
33814                 }
33815             }
33816             /** This region's collapsed element
33817             * @type Roo.Element */
33818             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33819                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33820             ]}, true);
33821             if(c.floatable !== false){
33822                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33823                this.collapsedEl.on("click", this.collapseClick, this);
33824             }
33825
33826             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33827                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33828                    id: "message", unselectable: "on", style:{"float":"left"}});
33829                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33830              }
33831             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33832             this.expandBtn.on("click", this.expand, this);
33833         }
33834         if(this.collapseBtn){
33835             this.collapseBtn.setVisible(c.collapsible == true);
33836         }
33837         this.cmargins = c.cmargins || this.cmargins ||
33838                          (this.position == "west" || this.position == "east" ?
33839                              {top: 0, left: 2, right:2, bottom: 0} :
33840                              {top: 2, left: 0, right:0, bottom: 2});
33841         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33842         this.bottomTabs = c.tabPosition != "top";
33843         this.autoScroll = c.autoScroll || false;
33844         if(this.autoScroll){
33845             this.bodyEl.setStyle("overflow", "auto");
33846         }else{
33847             this.bodyEl.setStyle("overflow", "hidden");
33848         }
33849         //if(c.titlebar !== false){
33850             if((!c.titlebar && !c.title) || c.titlebar === false){
33851                 this.titleEl.hide();
33852             }else{
33853                 this.titleEl.show();
33854                 if(c.title){
33855                     this.titleTextEl.innerHTML = c.title;
33856                 }
33857             }
33858         //}
33859         this.duration = c.duration || .30;
33860         this.slideDuration = c.slideDuration || .45;
33861         this.config = c;
33862         if(c.collapsed){
33863             this.collapse(true);
33864         }
33865         if(c.hidden){
33866             this.hide();
33867         }
33868     },
33869     /**
33870      * Returns true if this region is currently visible.
33871      * @return {Boolean}
33872      */
33873     isVisible : function(){
33874         return this.visible;
33875     },
33876
33877     /**
33878      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33879      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33880      */
33881     setCollapsedTitle : function(title){
33882         title = title || "&#160;";
33883         if(this.collapsedTitleTextEl){
33884             this.collapsedTitleTextEl.innerHTML = title;
33885         }
33886     },
33887
33888     getBox : function(){
33889         var b;
33890         if(!this.collapsed){
33891             b = this.el.getBox(false, true);
33892         }else{
33893             b = this.collapsedEl.getBox(false, true);
33894         }
33895         return b;
33896     },
33897
33898     getMargins : function(){
33899         return this.collapsed ? this.cmargins : this.margins;
33900     },
33901
33902     highlight : function(){
33903         this.el.addClass("x-layout-panel-dragover");
33904     },
33905
33906     unhighlight : function(){
33907         this.el.removeClass("x-layout-panel-dragover");
33908     },
33909
33910     updateBox : function(box){
33911         this.box = box;
33912         if(!this.collapsed){
33913             this.el.dom.style.left = box.x + "px";
33914             this.el.dom.style.top = box.y + "px";
33915             this.updateBody(box.width, box.height);
33916         }else{
33917             this.collapsedEl.dom.style.left = box.x + "px";
33918             this.collapsedEl.dom.style.top = box.y + "px";
33919             this.collapsedEl.setSize(box.width, box.height);
33920         }
33921         if(this.tabs){
33922             this.tabs.autoSizeTabs();
33923         }
33924     },
33925
33926     updateBody : function(w, h){
33927         if(w !== null){
33928             this.el.setWidth(w);
33929             w -= this.el.getBorderWidth("rl");
33930             if(this.config.adjustments){
33931                 w += this.config.adjustments[0];
33932             }
33933         }
33934         if(h !== null){
33935             this.el.setHeight(h);
33936             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33937             h -= this.el.getBorderWidth("tb");
33938             if(this.config.adjustments){
33939                 h += this.config.adjustments[1];
33940             }
33941             this.bodyEl.setHeight(h);
33942             if(this.tabs){
33943                 h = this.tabs.syncHeight(h);
33944             }
33945         }
33946         if(this.panelSize){
33947             w = w !== null ? w : this.panelSize.width;
33948             h = h !== null ? h : this.panelSize.height;
33949         }
33950         if(this.activePanel){
33951             var el = this.activePanel.getEl();
33952             w = w !== null ? w : el.getWidth();
33953             h = h !== null ? h : el.getHeight();
33954             this.panelSize = {width: w, height: h};
33955             this.activePanel.setSize(w, h);
33956         }
33957         if(Roo.isIE && this.tabs){
33958             this.tabs.el.repaint();
33959         }
33960     },
33961
33962     /**
33963      * Returns the container element for this region.
33964      * @return {Roo.Element}
33965      */
33966     getEl : function(){
33967         return this.el;
33968     },
33969
33970     /**
33971      * Hides this region.
33972      */
33973     hide : function(){
33974         if(!this.collapsed){
33975             this.el.dom.style.left = "-2000px";
33976             this.el.hide();
33977         }else{
33978             this.collapsedEl.dom.style.left = "-2000px";
33979             this.collapsedEl.hide();
33980         }
33981         this.visible = false;
33982         this.fireEvent("visibilitychange", this, false);
33983     },
33984
33985     /**
33986      * Shows this region if it was previously hidden.
33987      */
33988     show : function(){
33989         if(!this.collapsed){
33990             this.el.show();
33991         }else{
33992             this.collapsedEl.show();
33993         }
33994         this.visible = true;
33995         this.fireEvent("visibilitychange", this, true);
33996     },
33997
33998     closeClicked : function(){
33999         if(this.activePanel){
34000             this.remove(this.activePanel);
34001         }
34002     },
34003
34004     collapseClick : function(e){
34005         if(this.isSlid){
34006            e.stopPropagation();
34007            this.slideIn();
34008         }else{
34009            e.stopPropagation();
34010            this.slideOut();
34011         }
34012     },
34013
34014     /**
34015      * Collapses this region.
34016      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34017      */
34018     collapse : function(skipAnim){
34019         if(this.collapsed) return;
34020         this.collapsed = true;
34021         if(this.split){
34022             this.split.el.hide();
34023         }
34024         if(this.config.animate && skipAnim !== true){
34025             this.fireEvent("invalidated", this);
34026             this.animateCollapse();
34027         }else{
34028             this.el.setLocation(-20000,-20000);
34029             this.el.hide();
34030             this.collapsedEl.show();
34031             this.fireEvent("collapsed", this);
34032             this.fireEvent("invalidated", this);
34033         }
34034     },
34035
34036     animateCollapse : function(){
34037         // overridden
34038     },
34039
34040     /**
34041      * Expands this region if it was previously collapsed.
34042      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34043      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34044      */
34045     expand : function(e, skipAnim){
34046         if(e) e.stopPropagation();
34047         if(!this.collapsed || this.el.hasActiveFx()) return;
34048         if(this.isSlid){
34049             this.afterSlideIn();
34050             skipAnim = true;
34051         }
34052         this.collapsed = false;
34053         if(this.config.animate && skipAnim !== true){
34054             this.animateExpand();
34055         }else{
34056             this.el.show();
34057             if(this.split){
34058                 this.split.el.show();
34059             }
34060             this.collapsedEl.setLocation(-2000,-2000);
34061             this.collapsedEl.hide();
34062             this.fireEvent("invalidated", this);
34063             this.fireEvent("expanded", this);
34064         }
34065     },
34066
34067     animateExpand : function(){
34068         // overridden
34069     },
34070
34071     initTabs : function()
34072     {
34073         this.bodyEl.setStyle("overflow", "hidden");
34074         var ts = new Roo.TabPanel(
34075                 this.bodyEl.dom,
34076                 {
34077                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34078                     disableTooltips: this.config.disableTabTips,
34079                     toolbar : this.config.toolbar
34080                 }
34081         );
34082         if(this.config.hideTabs){
34083             ts.stripWrap.setDisplayed(false);
34084         }
34085         this.tabs = ts;
34086         ts.resizeTabs = this.config.resizeTabs === true;
34087         ts.minTabWidth = this.config.minTabWidth || 40;
34088         ts.maxTabWidth = this.config.maxTabWidth || 250;
34089         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34090         ts.monitorResize = false;
34091         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34092         ts.bodyEl.addClass('x-layout-tabs-body');
34093         this.panels.each(this.initPanelAsTab, this);
34094     },
34095
34096     initPanelAsTab : function(panel){
34097         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34098                     this.config.closeOnTab && panel.isClosable());
34099         if(panel.tabTip !== undefined){
34100             ti.setTooltip(panel.tabTip);
34101         }
34102         ti.on("activate", function(){
34103               this.setActivePanel(panel);
34104         }, this);
34105         if(this.config.closeOnTab){
34106             ti.on("beforeclose", function(t, e){
34107                 e.cancel = true;
34108                 this.remove(panel);
34109             }, this);
34110         }
34111         return ti;
34112     },
34113
34114     updatePanelTitle : function(panel, title){
34115         if(this.activePanel == panel){
34116             this.updateTitle(title);
34117         }
34118         if(this.tabs){
34119             var ti = this.tabs.getTab(panel.getEl().id);
34120             ti.setText(title);
34121             if(panel.tabTip !== undefined){
34122                 ti.setTooltip(panel.tabTip);
34123             }
34124         }
34125     },
34126
34127     updateTitle : function(title){
34128         if(this.titleTextEl && !this.config.title){
34129             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34130         }
34131     },
34132
34133     setActivePanel : function(panel){
34134         panel = this.getPanel(panel);
34135         if(this.activePanel && this.activePanel != panel){
34136             this.activePanel.setActiveState(false);
34137         }
34138         this.activePanel = panel;
34139         panel.setActiveState(true);
34140         if(this.panelSize){
34141             panel.setSize(this.panelSize.width, this.panelSize.height);
34142         }
34143         if(this.closeBtn){
34144             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34145         }
34146         this.updateTitle(panel.getTitle());
34147         if(this.tabs){
34148             this.fireEvent("invalidated", this);
34149         }
34150         this.fireEvent("panelactivated", this, panel);
34151     },
34152
34153     /**
34154      * Shows the specified panel.
34155      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34156      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34157      */
34158     showPanel : function(panel)
34159     {
34160         panel = this.getPanel(panel);
34161         if(panel){
34162             if(this.tabs){
34163                 var tab = this.tabs.getTab(panel.getEl().id);
34164                 if(tab.isHidden()){
34165                     this.tabs.unhideTab(tab.id);
34166                 }
34167                 tab.activate();
34168             }else{
34169                 this.setActivePanel(panel);
34170             }
34171         }
34172         return panel;
34173     },
34174
34175     /**
34176      * Get the active panel for this region.
34177      * @return {Roo.ContentPanel} The active panel or null
34178      */
34179     getActivePanel : function(){
34180         return this.activePanel;
34181     },
34182
34183     validateVisibility : function(){
34184         if(this.panels.getCount() < 1){
34185             this.updateTitle("&#160;");
34186             this.closeBtn.hide();
34187             this.hide();
34188         }else{
34189             if(!this.isVisible()){
34190                 this.show();
34191             }
34192         }
34193     },
34194
34195     /**
34196      * Adds the passed ContentPanel(s) to this region.
34197      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34198      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34199      */
34200     add : function(panel){
34201         if(arguments.length > 1){
34202             for(var i = 0, len = arguments.length; i < len; i++) {
34203                 this.add(arguments[i]);
34204             }
34205             return null;
34206         }
34207         if(this.hasPanel(panel)){
34208             this.showPanel(panel);
34209             return panel;
34210         }
34211         panel.setRegion(this);
34212         this.panels.add(panel);
34213         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34214             this.bodyEl.dom.appendChild(panel.getEl().dom);
34215             if(panel.background !== true){
34216                 this.setActivePanel(panel);
34217             }
34218             this.fireEvent("paneladded", this, panel);
34219             return panel;
34220         }
34221         if(!this.tabs){
34222             this.initTabs();
34223         }else{
34224             this.initPanelAsTab(panel);
34225         }
34226         if(panel.background !== true){
34227             this.tabs.activate(panel.getEl().id);
34228         }
34229         this.fireEvent("paneladded", this, panel);
34230         return panel;
34231     },
34232
34233     /**
34234      * Hides the tab for the specified panel.
34235      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34236      */
34237     hidePanel : function(panel){
34238         if(this.tabs && (panel = this.getPanel(panel))){
34239             this.tabs.hideTab(panel.getEl().id);
34240         }
34241     },
34242
34243     /**
34244      * Unhides the tab for a previously hidden panel.
34245      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34246      */
34247     unhidePanel : function(panel){
34248         if(this.tabs && (panel = this.getPanel(panel))){
34249             this.tabs.unhideTab(panel.getEl().id);
34250         }
34251     },
34252
34253     clearPanels : function(){
34254         while(this.panels.getCount() > 0){
34255              this.remove(this.panels.first());
34256         }
34257     },
34258
34259     /**
34260      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34261      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34262      * @param {Boolean} preservePanel Overrides the config preservePanel option
34263      * @return {Roo.ContentPanel} The panel that was removed
34264      */
34265     remove : function(panel, preservePanel){
34266         panel = this.getPanel(panel);
34267         if(!panel){
34268             return null;
34269         }
34270         var e = {};
34271         this.fireEvent("beforeremove", this, panel, e);
34272         if(e.cancel === true){
34273             return null;
34274         }
34275         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34276         var panelId = panel.getId();
34277         this.panels.removeKey(panelId);
34278         if(preservePanel){
34279             document.body.appendChild(panel.getEl().dom);
34280         }
34281         if(this.tabs){
34282             this.tabs.removeTab(panel.getEl().id);
34283         }else if (!preservePanel){
34284             this.bodyEl.dom.removeChild(panel.getEl().dom);
34285         }
34286         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34287             var p = this.panels.first();
34288             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34289             tempEl.appendChild(p.getEl().dom);
34290             this.bodyEl.update("");
34291             this.bodyEl.dom.appendChild(p.getEl().dom);
34292             tempEl = null;
34293             this.updateTitle(p.getTitle());
34294             this.tabs = null;
34295             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34296             this.setActivePanel(p);
34297         }
34298         panel.setRegion(null);
34299         if(this.activePanel == panel){
34300             this.activePanel = null;
34301         }
34302         if(this.config.autoDestroy !== false && preservePanel !== true){
34303             try{panel.destroy();}catch(e){}
34304         }
34305         this.fireEvent("panelremoved", this, panel);
34306         return panel;
34307     },
34308
34309     /**
34310      * Returns the TabPanel component used by this region
34311      * @return {Roo.TabPanel}
34312      */
34313     getTabs : function(){
34314         return this.tabs;
34315     },
34316
34317     createTool : function(parentEl, className){
34318         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34319             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34320         btn.addClassOnOver("x-layout-tools-button-over");
34321         return btn;
34322     }
34323 });/*
34324  * Based on:
34325  * Ext JS Library 1.1.1
34326  * Copyright(c) 2006-2007, Ext JS, LLC.
34327  *
34328  * Originally Released Under LGPL - original licence link has changed is not relivant.
34329  *
34330  * Fork - LGPL
34331  * <script type="text/javascript">
34332  */
34333  
34334
34335
34336 /**
34337  * @class Roo.SplitLayoutRegion
34338  * @extends Roo.LayoutRegion
34339  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34340  */
34341 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34342     this.cursor = cursor;
34343     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34344 };
34345
34346 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34347     splitTip : "Drag to resize.",
34348     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34349     useSplitTips : false,
34350
34351     applyConfig : function(config){
34352         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34353         if(config.split){
34354             if(!this.split){
34355                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34356                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34357                 /** The SplitBar for this region 
34358                 * @type Roo.SplitBar */
34359                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34360                 this.split.on("moved", this.onSplitMove, this);
34361                 this.split.useShim = config.useShim === true;
34362                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34363                 if(this.useSplitTips){
34364                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34365                 }
34366                 if(config.collapsible){
34367                     this.split.el.on("dblclick", this.collapse,  this);
34368                 }
34369             }
34370             if(typeof config.minSize != "undefined"){
34371                 this.split.minSize = config.minSize;
34372             }
34373             if(typeof config.maxSize != "undefined"){
34374                 this.split.maxSize = config.maxSize;
34375             }
34376             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34377                 this.hideSplitter();
34378             }
34379         }
34380     },
34381
34382     getHMaxSize : function(){
34383          var cmax = this.config.maxSize || 10000;
34384          var center = this.mgr.getRegion("center");
34385          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34386     },
34387
34388     getVMaxSize : function(){
34389          var cmax = this.config.maxSize || 10000;
34390          var center = this.mgr.getRegion("center");
34391          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34392     },
34393
34394     onSplitMove : function(split, newSize){
34395         this.fireEvent("resized", this, newSize);
34396     },
34397     
34398     /** 
34399      * Returns the {@link Roo.SplitBar} for this region.
34400      * @return {Roo.SplitBar}
34401      */
34402     getSplitBar : function(){
34403         return this.split;
34404     },
34405     
34406     hide : function(){
34407         this.hideSplitter();
34408         Roo.SplitLayoutRegion.superclass.hide.call(this);
34409     },
34410
34411     hideSplitter : function(){
34412         if(this.split){
34413             this.split.el.setLocation(-2000,-2000);
34414             this.split.el.hide();
34415         }
34416     },
34417
34418     show : function(){
34419         if(this.split){
34420             this.split.el.show();
34421         }
34422         Roo.SplitLayoutRegion.superclass.show.call(this);
34423     },
34424     
34425     beforeSlide: function(){
34426         if(Roo.isGecko){// firefox overflow auto bug workaround
34427             this.bodyEl.clip();
34428             if(this.tabs) this.tabs.bodyEl.clip();
34429             if(this.activePanel){
34430                 this.activePanel.getEl().clip();
34431                 
34432                 if(this.activePanel.beforeSlide){
34433                     this.activePanel.beforeSlide();
34434                 }
34435             }
34436         }
34437     },
34438     
34439     afterSlide : function(){
34440         if(Roo.isGecko){// firefox overflow auto bug workaround
34441             this.bodyEl.unclip();
34442             if(this.tabs) this.tabs.bodyEl.unclip();
34443             if(this.activePanel){
34444                 this.activePanel.getEl().unclip();
34445                 if(this.activePanel.afterSlide){
34446                     this.activePanel.afterSlide();
34447                 }
34448             }
34449         }
34450     },
34451
34452     initAutoHide : function(){
34453         if(this.autoHide !== false){
34454             if(!this.autoHideHd){
34455                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34456                 this.autoHideHd = {
34457                     "mouseout": function(e){
34458                         if(!e.within(this.el, true)){
34459                             st.delay(500);
34460                         }
34461                     },
34462                     "mouseover" : function(e){
34463                         st.cancel();
34464                     },
34465                     scope : this
34466                 };
34467             }
34468             this.el.on(this.autoHideHd);
34469         }
34470     },
34471
34472     clearAutoHide : function(){
34473         if(this.autoHide !== false){
34474             this.el.un("mouseout", this.autoHideHd.mouseout);
34475             this.el.un("mouseover", this.autoHideHd.mouseover);
34476         }
34477     },
34478
34479     clearMonitor : function(){
34480         Roo.get(document).un("click", this.slideInIf, this);
34481     },
34482
34483     // these names are backwards but not changed for compat
34484     slideOut : function(){
34485         if(this.isSlid || this.el.hasActiveFx()){
34486             return;
34487         }
34488         this.isSlid = true;
34489         if(this.collapseBtn){
34490             this.collapseBtn.hide();
34491         }
34492         this.closeBtnState = this.closeBtn.getStyle('display');
34493         this.closeBtn.hide();
34494         if(this.stickBtn){
34495             this.stickBtn.show();
34496         }
34497         this.el.show();
34498         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34499         this.beforeSlide();
34500         this.el.setStyle("z-index", 10001);
34501         this.el.slideIn(this.getSlideAnchor(), {
34502             callback: function(){
34503                 this.afterSlide();
34504                 this.initAutoHide();
34505                 Roo.get(document).on("click", this.slideInIf, this);
34506                 this.fireEvent("slideshow", this);
34507             },
34508             scope: this,
34509             block: true
34510         });
34511     },
34512
34513     afterSlideIn : function(){
34514         this.clearAutoHide();
34515         this.isSlid = false;
34516         this.clearMonitor();
34517         this.el.setStyle("z-index", "");
34518         if(this.collapseBtn){
34519             this.collapseBtn.show();
34520         }
34521         this.closeBtn.setStyle('display', this.closeBtnState);
34522         if(this.stickBtn){
34523             this.stickBtn.hide();
34524         }
34525         this.fireEvent("slidehide", this);
34526     },
34527
34528     slideIn : function(cb){
34529         if(!this.isSlid || this.el.hasActiveFx()){
34530             Roo.callback(cb);
34531             return;
34532         }
34533         this.isSlid = false;
34534         this.beforeSlide();
34535         this.el.slideOut(this.getSlideAnchor(), {
34536             callback: function(){
34537                 this.el.setLeftTop(-10000, -10000);
34538                 this.afterSlide();
34539                 this.afterSlideIn();
34540                 Roo.callback(cb);
34541             },
34542             scope: this,
34543             block: true
34544         });
34545     },
34546     
34547     slideInIf : function(e){
34548         if(!e.within(this.el)){
34549             this.slideIn();
34550         }
34551     },
34552
34553     animateCollapse : function(){
34554         this.beforeSlide();
34555         this.el.setStyle("z-index", 20000);
34556         var anchor = this.getSlideAnchor();
34557         this.el.slideOut(anchor, {
34558             callback : function(){
34559                 this.el.setStyle("z-index", "");
34560                 this.collapsedEl.slideIn(anchor, {duration:.3});
34561                 this.afterSlide();
34562                 this.el.setLocation(-10000,-10000);
34563                 this.el.hide();
34564                 this.fireEvent("collapsed", this);
34565             },
34566             scope: this,
34567             block: true
34568         });
34569     },
34570
34571     animateExpand : function(){
34572         this.beforeSlide();
34573         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34574         this.el.setStyle("z-index", 20000);
34575         this.collapsedEl.hide({
34576             duration:.1
34577         });
34578         this.el.slideIn(this.getSlideAnchor(), {
34579             callback : function(){
34580                 this.el.setStyle("z-index", "");
34581                 this.afterSlide();
34582                 if(this.split){
34583                     this.split.el.show();
34584                 }
34585                 this.fireEvent("invalidated", this);
34586                 this.fireEvent("expanded", this);
34587             },
34588             scope: this,
34589             block: true
34590         });
34591     },
34592
34593     anchors : {
34594         "west" : "left",
34595         "east" : "right",
34596         "north" : "top",
34597         "south" : "bottom"
34598     },
34599
34600     sanchors : {
34601         "west" : "l",
34602         "east" : "r",
34603         "north" : "t",
34604         "south" : "b"
34605     },
34606
34607     canchors : {
34608         "west" : "tl-tr",
34609         "east" : "tr-tl",
34610         "north" : "tl-bl",
34611         "south" : "bl-tl"
34612     },
34613
34614     getAnchor : function(){
34615         return this.anchors[this.position];
34616     },
34617
34618     getCollapseAnchor : function(){
34619         return this.canchors[this.position];
34620     },
34621
34622     getSlideAnchor : function(){
34623         return this.sanchors[this.position];
34624     },
34625
34626     getAlignAdj : function(){
34627         var cm = this.cmargins;
34628         switch(this.position){
34629             case "west":
34630                 return [0, 0];
34631             break;
34632             case "east":
34633                 return [0, 0];
34634             break;
34635             case "north":
34636                 return [0, 0];
34637             break;
34638             case "south":
34639                 return [0, 0];
34640             break;
34641         }
34642     },
34643
34644     getExpandAdj : function(){
34645         var c = this.collapsedEl, cm = this.cmargins;
34646         switch(this.position){
34647             case "west":
34648                 return [-(cm.right+c.getWidth()+cm.left), 0];
34649             break;
34650             case "east":
34651                 return [cm.right+c.getWidth()+cm.left, 0];
34652             break;
34653             case "north":
34654                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34655             break;
34656             case "south":
34657                 return [0, cm.top+cm.bottom+c.getHeight()];
34658             break;
34659         }
34660     }
34661 });/*
34662  * Based on:
34663  * Ext JS Library 1.1.1
34664  * Copyright(c) 2006-2007, Ext JS, LLC.
34665  *
34666  * Originally Released Under LGPL - original licence link has changed is not relivant.
34667  *
34668  * Fork - LGPL
34669  * <script type="text/javascript">
34670  */
34671 /*
34672  * These classes are private internal classes
34673  */
34674 Roo.CenterLayoutRegion = function(mgr, config){
34675     Roo.LayoutRegion.call(this, mgr, config, "center");
34676     this.visible = true;
34677     this.minWidth = config.minWidth || 20;
34678     this.minHeight = config.minHeight || 20;
34679 };
34680
34681 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34682     hide : function(){
34683         // center panel can't be hidden
34684     },
34685     
34686     show : function(){
34687         // center panel can't be hidden
34688     },
34689     
34690     getMinWidth: function(){
34691         return this.minWidth;
34692     },
34693     
34694     getMinHeight: function(){
34695         return this.minHeight;
34696     }
34697 });
34698
34699
34700 Roo.NorthLayoutRegion = function(mgr, config){
34701     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34702     if(this.split){
34703         this.split.placement = Roo.SplitBar.TOP;
34704         this.split.orientation = Roo.SplitBar.VERTICAL;
34705         this.split.el.addClass("x-layout-split-v");
34706     }
34707     var size = config.initialSize || config.height;
34708     if(typeof size != "undefined"){
34709         this.el.setHeight(size);
34710     }
34711 };
34712 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34713     orientation: Roo.SplitBar.VERTICAL,
34714     getBox : function(){
34715         if(this.collapsed){
34716             return this.collapsedEl.getBox();
34717         }
34718         var box = this.el.getBox();
34719         if(this.split){
34720             box.height += this.split.el.getHeight();
34721         }
34722         return box;
34723     },
34724     
34725     updateBox : function(box){
34726         if(this.split && !this.collapsed){
34727             box.height -= this.split.el.getHeight();
34728             this.split.el.setLeft(box.x);
34729             this.split.el.setTop(box.y+box.height);
34730             this.split.el.setWidth(box.width);
34731         }
34732         if(this.collapsed){
34733             this.updateBody(box.width, null);
34734         }
34735         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34736     }
34737 });
34738
34739 Roo.SouthLayoutRegion = function(mgr, config){
34740     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34741     if(this.split){
34742         this.split.placement = Roo.SplitBar.BOTTOM;
34743         this.split.orientation = Roo.SplitBar.VERTICAL;
34744         this.split.el.addClass("x-layout-split-v");
34745     }
34746     var size = config.initialSize || config.height;
34747     if(typeof size != "undefined"){
34748         this.el.setHeight(size);
34749     }
34750 };
34751 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34752     orientation: Roo.SplitBar.VERTICAL,
34753     getBox : function(){
34754         if(this.collapsed){
34755             return this.collapsedEl.getBox();
34756         }
34757         var box = this.el.getBox();
34758         if(this.split){
34759             var sh = this.split.el.getHeight();
34760             box.height += sh;
34761             box.y -= sh;
34762         }
34763         return box;
34764     },
34765     
34766     updateBox : function(box){
34767         if(this.split && !this.collapsed){
34768             var sh = this.split.el.getHeight();
34769             box.height -= sh;
34770             box.y += sh;
34771             this.split.el.setLeft(box.x);
34772             this.split.el.setTop(box.y-sh);
34773             this.split.el.setWidth(box.width);
34774         }
34775         if(this.collapsed){
34776             this.updateBody(box.width, null);
34777         }
34778         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34779     }
34780 });
34781
34782 Roo.EastLayoutRegion = function(mgr, config){
34783     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34784     if(this.split){
34785         this.split.placement = Roo.SplitBar.RIGHT;
34786         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34787         this.split.el.addClass("x-layout-split-h");
34788     }
34789     var size = config.initialSize || config.width;
34790     if(typeof size != "undefined"){
34791         this.el.setWidth(size);
34792     }
34793 };
34794 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34795     orientation: Roo.SplitBar.HORIZONTAL,
34796     getBox : function(){
34797         if(this.collapsed){
34798             return this.collapsedEl.getBox();
34799         }
34800         var box = this.el.getBox();
34801         if(this.split){
34802             var sw = this.split.el.getWidth();
34803             box.width += sw;
34804             box.x -= sw;
34805         }
34806         return box;
34807     },
34808
34809     updateBox : function(box){
34810         if(this.split && !this.collapsed){
34811             var sw = this.split.el.getWidth();
34812             box.width -= sw;
34813             this.split.el.setLeft(box.x);
34814             this.split.el.setTop(box.y);
34815             this.split.el.setHeight(box.height);
34816             box.x += sw;
34817         }
34818         if(this.collapsed){
34819             this.updateBody(null, box.height);
34820         }
34821         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34822     }
34823 });
34824
34825 Roo.WestLayoutRegion = function(mgr, config){
34826     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34827     if(this.split){
34828         this.split.placement = Roo.SplitBar.LEFT;
34829         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34830         this.split.el.addClass("x-layout-split-h");
34831     }
34832     var size = config.initialSize || config.width;
34833     if(typeof size != "undefined"){
34834         this.el.setWidth(size);
34835     }
34836 };
34837 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34838     orientation: Roo.SplitBar.HORIZONTAL,
34839     getBox : function(){
34840         if(this.collapsed){
34841             return this.collapsedEl.getBox();
34842         }
34843         var box = this.el.getBox();
34844         if(this.split){
34845             box.width += this.split.el.getWidth();
34846         }
34847         return box;
34848     },
34849     
34850     updateBox : function(box){
34851         if(this.split && !this.collapsed){
34852             var sw = this.split.el.getWidth();
34853             box.width -= sw;
34854             this.split.el.setLeft(box.x+box.width);
34855             this.split.el.setTop(box.y);
34856             this.split.el.setHeight(box.height);
34857         }
34858         if(this.collapsed){
34859             this.updateBody(null, box.height);
34860         }
34861         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34862     }
34863 });
34864 /*
34865  * Based on:
34866  * Ext JS Library 1.1.1
34867  * Copyright(c) 2006-2007, Ext JS, LLC.
34868  *
34869  * Originally Released Under LGPL - original licence link has changed is not relivant.
34870  *
34871  * Fork - LGPL
34872  * <script type="text/javascript">
34873  */
34874  
34875  
34876 /*
34877  * Private internal class for reading and applying state
34878  */
34879 Roo.LayoutStateManager = function(layout){
34880      // default empty state
34881      this.state = {
34882         north: {},
34883         south: {},
34884         east: {},
34885         west: {}       
34886     };
34887 };
34888
34889 Roo.LayoutStateManager.prototype = {
34890     init : function(layout, provider){
34891         this.provider = provider;
34892         var state = provider.get(layout.id+"-layout-state");
34893         if(state){
34894             var wasUpdating = layout.isUpdating();
34895             if(!wasUpdating){
34896                 layout.beginUpdate();
34897             }
34898             for(var key in state){
34899                 if(typeof state[key] != "function"){
34900                     var rstate = state[key];
34901                     var r = layout.getRegion(key);
34902                     if(r && rstate){
34903                         if(rstate.size){
34904                             r.resizeTo(rstate.size);
34905                         }
34906                         if(rstate.collapsed == true){
34907                             r.collapse(true);
34908                         }else{
34909                             r.expand(null, true);
34910                         }
34911                     }
34912                 }
34913             }
34914             if(!wasUpdating){
34915                 layout.endUpdate();
34916             }
34917             this.state = state; 
34918         }
34919         this.layout = layout;
34920         layout.on("regionresized", this.onRegionResized, this);
34921         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34922         layout.on("regionexpanded", this.onRegionExpanded, this);
34923     },
34924     
34925     storeState : function(){
34926         this.provider.set(this.layout.id+"-layout-state", this.state);
34927     },
34928     
34929     onRegionResized : function(region, newSize){
34930         this.state[region.getPosition()].size = newSize;
34931         this.storeState();
34932     },
34933     
34934     onRegionCollapsed : function(region){
34935         this.state[region.getPosition()].collapsed = true;
34936         this.storeState();
34937     },
34938     
34939     onRegionExpanded : function(region){
34940         this.state[region.getPosition()].collapsed = false;
34941         this.storeState();
34942     }
34943 };/*
34944  * Based on:
34945  * Ext JS Library 1.1.1
34946  * Copyright(c) 2006-2007, Ext JS, LLC.
34947  *
34948  * Originally Released Under LGPL - original licence link has changed is not relivant.
34949  *
34950  * Fork - LGPL
34951  * <script type="text/javascript">
34952  */
34953 /**
34954  * @class Roo.ContentPanel
34955  * @extends Roo.util.Observable
34956  * A basic ContentPanel element.
34957  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34958  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34959  * @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
34960  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34961  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34962  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34963  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34964  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34965  * @cfg {String} title          The title for this panel
34966  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34967  * @cfg {String} url            Calls {@link #setUrl} with this value
34968  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34969  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34970  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34971  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34972
34973  * @constructor
34974  * Create a new ContentPanel.
34975  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34976  * @param {String/Object} config A string to set only the title or a config object
34977  * @param {String} content (optional) Set the HTML content for this panel
34978  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34979  */
34980 Roo.ContentPanel = function(el, config, content){
34981     
34982      
34983     /*
34984     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34985         config = el;
34986         el = Roo.id();
34987     }
34988     if (config && config.parentLayout) { 
34989         el = config.parentLayout.el.createChild(); 
34990     }
34991     */
34992     if(el.autoCreate){ // xtype is available if this is called from factory
34993         config = el;
34994         el = Roo.id();
34995     }
34996     this.el = Roo.get(el);
34997     if(!this.el && config && config.autoCreate){
34998         if(typeof config.autoCreate == "object"){
34999             if(!config.autoCreate.id){
35000                 config.autoCreate.id = config.id||el;
35001             }
35002             this.el = Roo.DomHelper.append(document.body,
35003                         config.autoCreate, true);
35004         }else{
35005             this.el = Roo.DomHelper.append(document.body,
35006                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35007         }
35008     }
35009     this.closable = false;
35010     this.loaded = false;
35011     this.active = false;
35012     if(typeof config == "string"){
35013         this.title = config;
35014     }else{
35015         Roo.apply(this, config);
35016     }
35017     
35018     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35019         this.wrapEl = this.el.wrap();
35020         this.toolbar.container = this.el.insertSibling(false, 'before');
35021         this.toolbar = new Roo.Toolbar(this.toolbar);
35022     }
35023     
35024     // xtype created footer. - not sure if will work as we normally have to render first..
35025     if (this.footer && !this.footer.el && this.footer.xtype) {
35026         if (!this.wrapEl) {
35027             this.wrapEl = this.el.wrap();
35028         }
35029     
35030         this.footer.container = this.wrapEl.createChild();
35031          
35032         this.footer = Roo.factory(this.footer, Roo);
35033         
35034     }
35035     
35036     if(this.resizeEl){
35037         this.resizeEl = Roo.get(this.resizeEl, true);
35038     }else{
35039         this.resizeEl = this.el;
35040     }
35041     // handle view.xtype
35042     
35043  
35044     
35045     
35046     this.addEvents({
35047         /**
35048          * @event activate
35049          * Fires when this panel is activated. 
35050          * @param {Roo.ContentPanel} this
35051          */
35052         "activate" : true,
35053         /**
35054          * @event deactivate
35055          * Fires when this panel is activated. 
35056          * @param {Roo.ContentPanel} this
35057          */
35058         "deactivate" : true,
35059
35060         /**
35061          * @event resize
35062          * Fires when this panel is resized if fitToFrame is true.
35063          * @param {Roo.ContentPanel} this
35064          * @param {Number} width The width after any component adjustments
35065          * @param {Number} height The height after any component adjustments
35066          */
35067         "resize" : true,
35068         
35069          /**
35070          * @event render
35071          * Fires when this tab is created
35072          * @param {Roo.ContentPanel} this
35073          */
35074         "render" : true
35075         
35076         
35077         
35078     });
35079     
35080
35081     
35082     
35083     if(this.autoScroll){
35084         this.resizeEl.setStyle("overflow", "auto");
35085     } else {
35086         // fix randome scrolling
35087         this.el.on('scroll', function() {
35088             Roo.log('fix random scolling');
35089             this.scrollTo('top',0); 
35090         });
35091     }
35092     content = content || this.content;
35093     if(content){
35094         this.setContent(content);
35095     }
35096     if(config && config.url){
35097         this.setUrl(this.url, this.params, this.loadOnce);
35098     }
35099     
35100     
35101     
35102     Roo.ContentPanel.superclass.constructor.call(this);
35103     
35104     if (this.view && typeof(this.view.xtype) != 'undefined') {
35105         this.view.el = this.el.appendChild(document.createElement("div"));
35106         this.view = Roo.factory(this.view); 
35107         this.view.render  &&  this.view.render(false, '');  
35108     }
35109     
35110     
35111     this.fireEvent('render', this);
35112 };
35113
35114 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35115     tabTip:'',
35116     setRegion : function(region){
35117         this.region = region;
35118         if(region){
35119            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35120         }else{
35121            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35122         } 
35123     },
35124     
35125     /**
35126      * Returns the toolbar for this Panel if one was configured. 
35127      * @return {Roo.Toolbar} 
35128      */
35129     getToolbar : function(){
35130         return this.toolbar;
35131     },
35132     
35133     setActiveState : function(active){
35134         this.active = active;
35135         if(!active){
35136             this.fireEvent("deactivate", this);
35137         }else{
35138             this.fireEvent("activate", this);
35139         }
35140     },
35141     /**
35142      * Updates this panel's element
35143      * @param {String} content The new content
35144      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35145     */
35146     setContent : function(content, loadScripts){
35147         this.el.update(content, loadScripts);
35148     },
35149
35150     ignoreResize : function(w, h){
35151         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35152             return true;
35153         }else{
35154             this.lastSize = {width: w, height: h};
35155             return false;
35156         }
35157     },
35158     /**
35159      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35160      * @return {Roo.UpdateManager} The UpdateManager
35161      */
35162     getUpdateManager : function(){
35163         return this.el.getUpdateManager();
35164     },
35165      /**
35166      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35167      * @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:
35168 <pre><code>
35169 panel.load({
35170     url: "your-url.php",
35171     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35172     callback: yourFunction,
35173     scope: yourObject, //(optional scope)
35174     discardUrl: false,
35175     nocache: false,
35176     text: "Loading...",
35177     timeout: 30,
35178     scripts: false
35179 });
35180 </code></pre>
35181      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35182      * 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.
35183      * @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}
35184      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35185      * @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.
35186      * @return {Roo.ContentPanel} this
35187      */
35188     load : function(){
35189         var um = this.el.getUpdateManager();
35190         um.update.apply(um, arguments);
35191         return this;
35192     },
35193
35194
35195     /**
35196      * 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.
35197      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35198      * @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)
35199      * @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)
35200      * @return {Roo.UpdateManager} The UpdateManager
35201      */
35202     setUrl : function(url, params, loadOnce){
35203         if(this.refreshDelegate){
35204             this.removeListener("activate", this.refreshDelegate);
35205         }
35206         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35207         this.on("activate", this.refreshDelegate);
35208         return this.el.getUpdateManager();
35209     },
35210     
35211     _handleRefresh : function(url, params, loadOnce){
35212         if(!loadOnce || !this.loaded){
35213             var updater = this.el.getUpdateManager();
35214             updater.update(url, params, this._setLoaded.createDelegate(this));
35215         }
35216     },
35217     
35218     _setLoaded : function(){
35219         this.loaded = true;
35220     }, 
35221     
35222     /**
35223      * Returns this panel's id
35224      * @return {String} 
35225      */
35226     getId : function(){
35227         return this.el.id;
35228     },
35229     
35230     /** 
35231      * Returns this panel's element - used by regiosn to add.
35232      * @return {Roo.Element} 
35233      */
35234     getEl : function(){
35235         return this.wrapEl || this.el;
35236     },
35237     
35238     adjustForComponents : function(width, height)
35239     {
35240         //Roo.log('adjustForComponents ');
35241         if(this.resizeEl != this.el){
35242             width -= this.el.getFrameWidth('lr');
35243             height -= this.el.getFrameWidth('tb');
35244         }
35245         if(this.toolbar){
35246             var te = this.toolbar.getEl();
35247             height -= te.getHeight();
35248             te.setWidth(width);
35249         }
35250         if(this.footer){
35251             var te = this.footer.getEl();
35252             Roo.log("footer:" + te.getHeight());
35253             
35254             height -= te.getHeight();
35255             te.setWidth(width);
35256         }
35257         
35258         
35259         if(this.adjustments){
35260             width += this.adjustments[0];
35261             height += this.adjustments[1];
35262         }
35263         return {"width": width, "height": height};
35264     },
35265     
35266     setSize : function(width, height){
35267         if(this.fitToFrame && !this.ignoreResize(width, height)){
35268             if(this.fitContainer && this.resizeEl != this.el){
35269                 this.el.setSize(width, height);
35270             }
35271             var size = this.adjustForComponents(width, height);
35272             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35273             this.fireEvent('resize', this, size.width, size.height);
35274         }
35275     },
35276     
35277     /**
35278      * Returns this panel's title
35279      * @return {String} 
35280      */
35281     getTitle : function(){
35282         return this.title;
35283     },
35284     
35285     /**
35286      * Set this panel's title
35287      * @param {String} title
35288      */
35289     setTitle : function(title){
35290         this.title = title;
35291         if(this.region){
35292             this.region.updatePanelTitle(this, title);
35293         }
35294     },
35295     
35296     /**
35297      * Returns true is this panel was configured to be closable
35298      * @return {Boolean} 
35299      */
35300     isClosable : function(){
35301         return this.closable;
35302     },
35303     
35304     beforeSlide : function(){
35305         this.el.clip();
35306         this.resizeEl.clip();
35307     },
35308     
35309     afterSlide : function(){
35310         this.el.unclip();
35311         this.resizeEl.unclip();
35312     },
35313     
35314     /**
35315      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35316      *   Will fail silently if the {@link #setUrl} method has not been called.
35317      *   This does not activate the panel, just updates its content.
35318      */
35319     refresh : function(){
35320         if(this.refreshDelegate){
35321            this.loaded = false;
35322            this.refreshDelegate();
35323         }
35324     },
35325     
35326     /**
35327      * Destroys this panel
35328      */
35329     destroy : function(){
35330         this.el.removeAllListeners();
35331         var tempEl = document.createElement("span");
35332         tempEl.appendChild(this.el.dom);
35333         tempEl.innerHTML = "";
35334         this.el.remove();
35335         this.el = null;
35336     },
35337     
35338     /**
35339      * form - if the content panel contains a form - this is a reference to it.
35340      * @type {Roo.form.Form}
35341      */
35342     form : false,
35343     /**
35344      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35345      *    This contains a reference to it.
35346      * @type {Roo.View}
35347      */
35348     view : false,
35349     
35350       /**
35351      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35352      * <pre><code>
35353
35354 layout.addxtype({
35355        xtype : 'Form',
35356        items: [ .... ]
35357    }
35358 );
35359
35360 </code></pre>
35361      * @param {Object} cfg Xtype definition of item to add.
35362      */
35363     
35364     addxtype : function(cfg) {
35365         // add form..
35366         if (cfg.xtype.match(/^Form$/)) {
35367             
35368             var el;
35369             //if (this.footer) {
35370             //    el = this.footer.container.insertSibling(false, 'before');
35371             //} else {
35372                 el = this.el.createChild();
35373             //}
35374
35375             this.form = new  Roo.form.Form(cfg);
35376             
35377             
35378             if ( this.form.allItems.length) this.form.render(el.dom);
35379             return this.form;
35380         }
35381         // should only have one of theses..
35382         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35383             // views.. should not be just added - used named prop 'view''
35384             
35385             cfg.el = this.el.appendChild(document.createElement("div"));
35386             // factory?
35387             
35388             var ret = new Roo.factory(cfg);
35389              
35390              ret.render && ret.render(false, ''); // render blank..
35391             this.view = ret;
35392             return ret;
35393         }
35394         return false;
35395     }
35396 });
35397
35398 /**
35399  * @class Roo.GridPanel
35400  * @extends Roo.ContentPanel
35401  * @constructor
35402  * Create a new GridPanel.
35403  * @param {Roo.grid.Grid} grid The grid for this panel
35404  * @param {String/Object} config A string to set only the panel's title, or a config object
35405  */
35406 Roo.GridPanel = function(grid, config){
35407     
35408   
35409     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35410         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35411         
35412     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35413     
35414     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35415     
35416     if(this.toolbar){
35417         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35418     }
35419     // xtype created footer. - not sure if will work as we normally have to render first..
35420     if (this.footer && !this.footer.el && this.footer.xtype) {
35421         
35422         this.footer.container = this.grid.getView().getFooterPanel(true);
35423         this.footer.dataSource = this.grid.dataSource;
35424         this.footer = Roo.factory(this.footer, Roo);
35425         
35426     }
35427     
35428     grid.monitorWindowResize = false; // turn off autosizing
35429     grid.autoHeight = false;
35430     grid.autoWidth = false;
35431     this.grid = grid;
35432     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35433 };
35434
35435 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35436     getId : function(){
35437         return this.grid.id;
35438     },
35439     
35440     /**
35441      * Returns the grid for this panel
35442      * @return {Roo.grid.Grid} 
35443      */
35444     getGrid : function(){
35445         return this.grid;    
35446     },
35447     
35448     setSize : function(width, height){
35449         if(!this.ignoreResize(width, height)){
35450             var grid = this.grid;
35451             var size = this.adjustForComponents(width, height);
35452             grid.getGridEl().setSize(size.width, size.height);
35453             grid.autoSize();
35454         }
35455     },
35456     
35457     beforeSlide : function(){
35458         this.grid.getView().scroller.clip();
35459     },
35460     
35461     afterSlide : function(){
35462         this.grid.getView().scroller.unclip();
35463     },
35464     
35465     destroy : function(){
35466         this.grid.destroy();
35467         delete this.grid;
35468         Roo.GridPanel.superclass.destroy.call(this); 
35469     }
35470 });
35471
35472
35473 /**
35474  * @class Roo.NestedLayoutPanel
35475  * @extends Roo.ContentPanel
35476  * @constructor
35477  * Create a new NestedLayoutPanel.
35478  * 
35479  * 
35480  * @param {Roo.BorderLayout} layout The layout for this panel
35481  * @param {String/Object} config A string to set only the title or a config object
35482  */
35483 Roo.NestedLayoutPanel = function(layout, config)
35484 {
35485     // construct with only one argument..
35486     /* FIXME - implement nicer consturctors
35487     if (layout.layout) {
35488         config = layout;
35489         layout = config.layout;
35490         delete config.layout;
35491     }
35492     if (layout.xtype && !layout.getEl) {
35493         // then layout needs constructing..
35494         layout = Roo.factory(layout, Roo);
35495     }
35496     */
35497     
35498     
35499     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35500     
35501     layout.monitorWindowResize = false; // turn off autosizing
35502     this.layout = layout;
35503     this.layout.getEl().addClass("x-layout-nested-layout");
35504     
35505     
35506     
35507     
35508 };
35509
35510 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35511
35512     setSize : function(width, height){
35513         if(!this.ignoreResize(width, height)){
35514             var size = this.adjustForComponents(width, height);
35515             var el = this.layout.getEl();
35516             el.setSize(size.width, size.height);
35517             var touch = el.dom.offsetWidth;
35518             this.layout.layout();
35519             // ie requires a double layout on the first pass
35520             if(Roo.isIE && !this.initialized){
35521                 this.initialized = true;
35522                 this.layout.layout();
35523             }
35524         }
35525     },
35526     
35527     // activate all subpanels if not currently active..
35528     
35529     setActiveState : function(active){
35530         this.active = active;
35531         if(!active){
35532             this.fireEvent("deactivate", this);
35533             return;
35534         }
35535         
35536         this.fireEvent("activate", this);
35537         // not sure if this should happen before or after..
35538         if (!this.layout) {
35539             return; // should not happen..
35540         }
35541         var reg = false;
35542         for (var r in this.layout.regions) {
35543             reg = this.layout.getRegion(r);
35544             if (reg.getActivePanel()) {
35545                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35546                 reg.setActivePanel(reg.getActivePanel());
35547                 continue;
35548             }
35549             if (!reg.panels.length) {
35550                 continue;
35551             }
35552             reg.showPanel(reg.getPanel(0));
35553         }
35554         
35555         
35556         
35557         
35558     },
35559     
35560     /**
35561      * Returns the nested BorderLayout for this panel
35562      * @return {Roo.BorderLayout} 
35563      */
35564     getLayout : function(){
35565         return this.layout;
35566     },
35567     
35568      /**
35569      * Adds a xtype elements to the layout of the nested panel
35570      * <pre><code>
35571
35572 panel.addxtype({
35573        xtype : 'ContentPanel',
35574        region: 'west',
35575        items: [ .... ]
35576    }
35577 );
35578
35579 panel.addxtype({
35580         xtype : 'NestedLayoutPanel',
35581         region: 'west',
35582         layout: {
35583            center: { },
35584            west: { }   
35585         },
35586         items : [ ... list of content panels or nested layout panels.. ]
35587    }
35588 );
35589 </code></pre>
35590      * @param {Object} cfg Xtype definition of item to add.
35591      */
35592     addxtype : function(cfg) {
35593         return this.layout.addxtype(cfg);
35594     
35595     }
35596 });
35597
35598 Roo.ScrollPanel = function(el, config, content){
35599     config = config || {};
35600     config.fitToFrame = true;
35601     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35602     
35603     this.el.dom.style.overflow = "hidden";
35604     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35605     this.el.removeClass("x-layout-inactive-content");
35606     this.el.on("mousewheel", this.onWheel, this);
35607
35608     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35609     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35610     up.unselectable(); down.unselectable();
35611     up.on("click", this.scrollUp, this);
35612     down.on("click", this.scrollDown, this);
35613     up.addClassOnOver("x-scroller-btn-over");
35614     down.addClassOnOver("x-scroller-btn-over");
35615     up.addClassOnClick("x-scroller-btn-click");
35616     down.addClassOnClick("x-scroller-btn-click");
35617     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35618
35619     this.resizeEl = this.el;
35620     this.el = wrap; this.up = up; this.down = down;
35621 };
35622
35623 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35624     increment : 100,
35625     wheelIncrement : 5,
35626     scrollUp : function(){
35627         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35628     },
35629
35630     scrollDown : function(){
35631         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35632     },
35633
35634     afterScroll : function(){
35635         var el = this.resizeEl;
35636         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35637         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35638         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35639     },
35640
35641     setSize : function(){
35642         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35643         this.afterScroll();
35644     },
35645
35646     onWheel : function(e){
35647         var d = e.getWheelDelta();
35648         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35649         this.afterScroll();
35650         e.stopEvent();
35651     },
35652
35653     setContent : function(content, loadScripts){
35654         this.resizeEl.update(content, loadScripts);
35655     }
35656
35657 });
35658
35659
35660
35661
35662
35663
35664
35665
35666
35667 /**
35668  * @class Roo.TreePanel
35669  * @extends Roo.ContentPanel
35670  * @constructor
35671  * Create a new TreePanel. - defaults to fit/scoll contents.
35672  * @param {String/Object} config A string to set only the panel's title, or a config object
35673  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35674  */
35675 Roo.TreePanel = function(config){
35676     var el = config.el;
35677     var tree = config.tree;
35678     delete config.tree; 
35679     delete config.el; // hopefull!
35680     
35681     // wrapper for IE7 strict & safari scroll issue
35682     
35683     var treeEl = el.createChild();
35684     config.resizeEl = treeEl;
35685     
35686     
35687     
35688     Roo.TreePanel.superclass.constructor.call(this, el, config);
35689  
35690  
35691     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35692     //console.log(tree);
35693     this.on('activate', function()
35694     {
35695         if (this.tree.rendered) {
35696             return;
35697         }
35698         //console.log('render tree');
35699         this.tree.render();
35700     });
35701     // this should not be needed.. - it's actually the 'el' that resizes?
35702     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35703     
35704     //this.on('resize',  function (cp, w, h) {
35705     //        this.tree.innerCt.setWidth(w);
35706     //        this.tree.innerCt.setHeight(h);
35707     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35708     //});
35709
35710         
35711     
35712 };
35713
35714 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35715     fitToFrame : true,
35716     autoScroll : true
35717 });
35718
35719
35720
35721
35722
35723
35724
35725
35726
35727
35728
35729 /*
35730  * Based on:
35731  * Ext JS Library 1.1.1
35732  * Copyright(c) 2006-2007, Ext JS, LLC.
35733  *
35734  * Originally Released Under LGPL - original licence link has changed is not relivant.
35735  *
35736  * Fork - LGPL
35737  * <script type="text/javascript">
35738  */
35739  
35740
35741 /**
35742  * @class Roo.ReaderLayout
35743  * @extends Roo.BorderLayout
35744  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35745  * center region containing two nested regions (a top one for a list view and one for item preview below),
35746  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35747  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35748  * expedites the setup of the overall layout and regions for this common application style.
35749  * Example:
35750  <pre><code>
35751 var reader = new Roo.ReaderLayout();
35752 var CP = Roo.ContentPanel;  // shortcut for adding
35753
35754 reader.beginUpdate();
35755 reader.add("north", new CP("north", "North"));
35756 reader.add("west", new CP("west", {title: "West"}));
35757 reader.add("east", new CP("east", {title: "East"}));
35758
35759 reader.regions.listView.add(new CP("listView", "List"));
35760 reader.regions.preview.add(new CP("preview", "Preview"));
35761 reader.endUpdate();
35762 </code></pre>
35763 * @constructor
35764 * Create a new ReaderLayout
35765 * @param {Object} config Configuration options
35766 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35767 * document.body if omitted)
35768 */
35769 Roo.ReaderLayout = function(config, renderTo){
35770     var c = config || {size:{}};
35771     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35772         north: c.north !== false ? Roo.apply({
35773             split:false,
35774             initialSize: 32,
35775             titlebar: false
35776         }, c.north) : false,
35777         west: c.west !== false ? Roo.apply({
35778             split:true,
35779             initialSize: 200,
35780             minSize: 175,
35781             maxSize: 400,
35782             titlebar: true,
35783             collapsible: true,
35784             animate: true,
35785             margins:{left:5,right:0,bottom:5,top:5},
35786             cmargins:{left:5,right:5,bottom:5,top:5}
35787         }, c.west) : false,
35788         east: c.east !== false ? Roo.apply({
35789             split:true,
35790             initialSize: 200,
35791             minSize: 175,
35792             maxSize: 400,
35793             titlebar: true,
35794             collapsible: true,
35795             animate: true,
35796             margins:{left:0,right:5,bottom:5,top:5},
35797             cmargins:{left:5,right:5,bottom:5,top:5}
35798         }, c.east) : false,
35799         center: Roo.apply({
35800             tabPosition: 'top',
35801             autoScroll:false,
35802             closeOnTab: true,
35803             titlebar:false,
35804             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35805         }, c.center)
35806     });
35807
35808     this.el.addClass('x-reader');
35809
35810     this.beginUpdate();
35811
35812     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35813         south: c.preview !== false ? Roo.apply({
35814             split:true,
35815             initialSize: 200,
35816             minSize: 100,
35817             autoScroll:true,
35818             collapsible:true,
35819             titlebar: true,
35820             cmargins:{top:5,left:0, right:0, bottom:0}
35821         }, c.preview) : false,
35822         center: Roo.apply({
35823             autoScroll:false,
35824             titlebar:false,
35825             minHeight:200
35826         }, c.listView)
35827     });
35828     this.add('center', new Roo.NestedLayoutPanel(inner,
35829             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35830
35831     this.endUpdate();
35832
35833     this.regions.preview = inner.getRegion('south');
35834     this.regions.listView = inner.getRegion('center');
35835 };
35836
35837 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35838  * Based on:
35839  * Ext JS Library 1.1.1
35840  * Copyright(c) 2006-2007, Ext JS, LLC.
35841  *
35842  * Originally Released Under LGPL - original licence link has changed is not relivant.
35843  *
35844  * Fork - LGPL
35845  * <script type="text/javascript">
35846  */
35847  
35848 /**
35849  * @class Roo.grid.Grid
35850  * @extends Roo.util.Observable
35851  * This class represents the primary interface of a component based grid control.
35852  * <br><br>Usage:<pre><code>
35853  var grid = new Roo.grid.Grid("my-container-id", {
35854      ds: myDataStore,
35855      cm: myColModel,
35856      selModel: mySelectionModel,
35857      autoSizeColumns: true,
35858      monitorWindowResize: false,
35859      trackMouseOver: true
35860  });
35861  // set any options
35862  grid.render();
35863  * </code></pre>
35864  * <b>Common Problems:</b><br/>
35865  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35866  * element will correct this<br/>
35867  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35868  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35869  * are unpredictable.<br/>
35870  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35871  * grid to calculate dimensions/offsets.<br/>
35872   * @constructor
35873  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35874  * The container MUST have some type of size defined for the grid to fill. The container will be
35875  * automatically set to position relative if it isn't already.
35876  * @param {Object} config A config object that sets properties on this grid.
35877  */
35878 Roo.grid.Grid = function(container, config){
35879         // initialize the container
35880         this.container = Roo.get(container);
35881         this.container.update("");
35882         this.container.setStyle("overflow", "hidden");
35883     this.container.addClass('x-grid-container');
35884
35885     this.id = this.container.id;
35886
35887     Roo.apply(this, config);
35888     // check and correct shorthanded configs
35889     if(this.ds){
35890         this.dataSource = this.ds;
35891         delete this.ds;
35892     }
35893     if(this.cm){
35894         this.colModel = this.cm;
35895         delete this.cm;
35896     }
35897     if(this.sm){
35898         this.selModel = this.sm;
35899         delete this.sm;
35900     }
35901
35902     if (this.selModel) {
35903         this.selModel = Roo.factory(this.selModel, Roo.grid);
35904         this.sm = this.selModel;
35905         this.sm.xmodule = this.xmodule || false;
35906     }
35907     if (typeof(this.colModel.config) == 'undefined') {
35908         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35909         this.cm = this.colModel;
35910         this.cm.xmodule = this.xmodule || false;
35911     }
35912     if (this.dataSource) {
35913         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35914         this.ds = this.dataSource;
35915         this.ds.xmodule = this.xmodule || false;
35916          
35917     }
35918     
35919     
35920     
35921     if(this.width){
35922         this.container.setWidth(this.width);
35923     }
35924
35925     if(this.height){
35926         this.container.setHeight(this.height);
35927     }
35928     /** @private */
35929         this.addEvents({
35930         // raw events
35931         /**
35932          * @event click
35933          * The raw click event for the entire grid.
35934          * @param {Roo.EventObject} e
35935          */
35936         "click" : true,
35937         /**
35938          * @event dblclick
35939          * The raw dblclick event for the entire grid.
35940          * @param {Roo.EventObject} e
35941          */
35942         "dblclick" : true,
35943         /**
35944          * @event contextmenu
35945          * The raw contextmenu event for the entire grid.
35946          * @param {Roo.EventObject} e
35947          */
35948         "contextmenu" : true,
35949         /**
35950          * @event mousedown
35951          * The raw mousedown event for the entire grid.
35952          * @param {Roo.EventObject} e
35953          */
35954         "mousedown" : true,
35955         /**
35956          * @event mouseup
35957          * The raw mouseup event for the entire grid.
35958          * @param {Roo.EventObject} e
35959          */
35960         "mouseup" : true,
35961         /**
35962          * @event mouseover
35963          * The raw mouseover event for the entire grid.
35964          * @param {Roo.EventObject} e
35965          */
35966         "mouseover" : true,
35967         /**
35968          * @event mouseout
35969          * The raw mouseout event for the entire grid.
35970          * @param {Roo.EventObject} e
35971          */
35972         "mouseout" : true,
35973         /**
35974          * @event keypress
35975          * The raw keypress event for the entire grid.
35976          * @param {Roo.EventObject} e
35977          */
35978         "keypress" : true,
35979         /**
35980          * @event keydown
35981          * The raw keydown event for the entire grid.
35982          * @param {Roo.EventObject} e
35983          */
35984         "keydown" : true,
35985
35986         // custom events
35987
35988         /**
35989          * @event cellclick
35990          * Fires when a cell is clicked
35991          * @param {Grid} this
35992          * @param {Number} rowIndex
35993          * @param {Number} columnIndex
35994          * @param {Roo.EventObject} e
35995          */
35996         "cellclick" : true,
35997         /**
35998          * @event celldblclick
35999          * Fires when a cell is double clicked
36000          * @param {Grid} this
36001          * @param {Number} rowIndex
36002          * @param {Number} columnIndex
36003          * @param {Roo.EventObject} e
36004          */
36005         "celldblclick" : true,
36006         /**
36007          * @event rowclick
36008          * Fires when a row is clicked
36009          * @param {Grid} this
36010          * @param {Number} rowIndex
36011          * @param {Roo.EventObject} e
36012          */
36013         "rowclick" : true,
36014         /**
36015          * @event rowdblclick
36016          * Fires when a row is double clicked
36017          * @param {Grid} this
36018          * @param {Number} rowIndex
36019          * @param {Roo.EventObject} e
36020          */
36021         "rowdblclick" : true,
36022         /**
36023          * @event headerclick
36024          * Fires when a header is clicked
36025          * @param {Grid} this
36026          * @param {Number} columnIndex
36027          * @param {Roo.EventObject} e
36028          */
36029         "headerclick" : true,
36030         /**
36031          * @event headerdblclick
36032          * Fires when a header cell is double clicked
36033          * @param {Grid} this
36034          * @param {Number} columnIndex
36035          * @param {Roo.EventObject} e
36036          */
36037         "headerdblclick" : true,
36038         /**
36039          * @event rowcontextmenu
36040          * Fires when a row is right clicked
36041          * @param {Grid} this
36042          * @param {Number} rowIndex
36043          * @param {Roo.EventObject} e
36044          */
36045         "rowcontextmenu" : true,
36046         /**
36047          * @event cellcontextmenu
36048          * Fires when a cell is right clicked
36049          * @param {Grid} this
36050          * @param {Number} rowIndex
36051          * @param {Number} cellIndex
36052          * @param {Roo.EventObject} e
36053          */
36054          "cellcontextmenu" : true,
36055         /**
36056          * @event headercontextmenu
36057          * Fires when a header is right clicked
36058          * @param {Grid} this
36059          * @param {Number} columnIndex
36060          * @param {Roo.EventObject} e
36061          */
36062         "headercontextmenu" : true,
36063         /**
36064          * @event bodyscroll
36065          * Fires when the body element is scrolled
36066          * @param {Number} scrollLeft
36067          * @param {Number} scrollTop
36068          */
36069         "bodyscroll" : true,
36070         /**
36071          * @event columnresize
36072          * Fires when the user resizes a column
36073          * @param {Number} columnIndex
36074          * @param {Number} newSize
36075          */
36076         "columnresize" : true,
36077         /**
36078          * @event columnmove
36079          * Fires when the user moves a column
36080          * @param {Number} oldIndex
36081          * @param {Number} newIndex
36082          */
36083         "columnmove" : true,
36084         /**
36085          * @event startdrag
36086          * Fires when row(s) start being dragged
36087          * @param {Grid} this
36088          * @param {Roo.GridDD} dd The drag drop object
36089          * @param {event} e The raw browser event
36090          */
36091         "startdrag" : true,
36092         /**
36093          * @event enddrag
36094          * Fires when a drag operation is complete
36095          * @param {Grid} this
36096          * @param {Roo.GridDD} dd The drag drop object
36097          * @param {event} e The raw browser event
36098          */
36099         "enddrag" : true,
36100         /**
36101          * @event dragdrop
36102          * Fires when dragged row(s) are dropped on a valid DD target
36103          * @param {Grid} this
36104          * @param {Roo.GridDD} dd The drag drop object
36105          * @param {String} targetId The target drag drop object
36106          * @param {event} e The raw browser event
36107          */
36108         "dragdrop" : true,
36109         /**
36110          * @event dragover
36111          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36112          * @param {Grid} this
36113          * @param {Roo.GridDD} dd The drag drop object
36114          * @param {String} targetId The target drag drop object
36115          * @param {event} e The raw browser event
36116          */
36117         "dragover" : true,
36118         /**
36119          * @event dragenter
36120          *  Fires when the dragged row(s) first cross another DD target while being dragged
36121          * @param {Grid} this
36122          * @param {Roo.GridDD} dd The drag drop object
36123          * @param {String} targetId The target drag drop object
36124          * @param {event} e The raw browser event
36125          */
36126         "dragenter" : true,
36127         /**
36128          * @event dragout
36129          * Fires when the dragged row(s) leave another DD target while being dragged
36130          * @param {Grid} this
36131          * @param {Roo.GridDD} dd The drag drop object
36132          * @param {String} targetId The target drag drop object
36133          * @param {event} e The raw browser event
36134          */
36135         "dragout" : true,
36136         /**
36137          * @event rowclass
36138          * Fires when a row is rendered, so you can change add a style to it.
36139          * @param {GridView} gridview   The grid view
36140          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36141          */
36142         'rowclass' : true,
36143
36144         /**
36145          * @event render
36146          * Fires when the grid is rendered
36147          * @param {Grid} grid
36148          */
36149         'render' : true
36150     });
36151
36152     Roo.grid.Grid.superclass.constructor.call(this);
36153 };
36154 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36155     
36156     /**
36157      * @cfg {String} ddGroup - drag drop group.
36158      */
36159
36160     /**
36161      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36162      */
36163     minColumnWidth : 25,
36164
36165     /**
36166      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36167      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36168      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36169      */
36170     autoSizeColumns : false,
36171
36172     /**
36173      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36174      */
36175     autoSizeHeaders : true,
36176
36177     /**
36178      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36179      */
36180     monitorWindowResize : true,
36181
36182     /**
36183      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36184      * rows measured to get a columns size. Default is 0 (all rows).
36185      */
36186     maxRowsToMeasure : 0,
36187
36188     /**
36189      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36190      */
36191     trackMouseOver : true,
36192
36193     /**
36194     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36195     */
36196     
36197     /**
36198     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36199     */
36200     enableDragDrop : false,
36201     
36202     /**
36203     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36204     */
36205     enableColumnMove : true,
36206     
36207     /**
36208     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36209     */
36210     enableColumnHide : true,
36211     
36212     /**
36213     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36214     */
36215     enableRowHeightSync : false,
36216     
36217     /**
36218     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36219     */
36220     stripeRows : true,
36221     
36222     /**
36223     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36224     */
36225     autoHeight : false,
36226
36227     /**
36228      * @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.
36229      */
36230     autoExpandColumn : false,
36231
36232     /**
36233     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36234     * Default is 50.
36235     */
36236     autoExpandMin : 50,
36237
36238     /**
36239     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36240     */
36241     autoExpandMax : 1000,
36242
36243     /**
36244     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36245     */
36246     view : null,
36247
36248     /**
36249     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36250     */
36251     loadMask : false,
36252     /**
36253     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36254     */
36255     dropTarget: false,
36256     
36257    
36258     
36259     // private
36260     rendered : false,
36261
36262     /**
36263     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36264     * of a fixed width. Default is false.
36265     */
36266     /**
36267     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36268     */
36269     /**
36270      * Called once after all setup has been completed and the grid is ready to be rendered.
36271      * @return {Roo.grid.Grid} this
36272      */
36273     render : function()
36274     {
36275         var c = this.container;
36276         // try to detect autoHeight/width mode
36277         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36278             this.autoHeight = true;
36279         }
36280         var view = this.getView();
36281         view.init(this);
36282
36283         c.on("click", this.onClick, this);
36284         c.on("dblclick", this.onDblClick, this);
36285         c.on("contextmenu", this.onContextMenu, this);
36286         c.on("keydown", this.onKeyDown, this);
36287         if (Roo.isTouch) {
36288             c.on("touchstart", this.onTouchStart, this);
36289         }
36290
36291         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36292
36293         this.getSelectionModel().init(this);
36294
36295         view.render();
36296
36297         if(this.loadMask){
36298             this.loadMask = new Roo.LoadMask(this.container,
36299                     Roo.apply({store:this.dataSource}, this.loadMask));
36300         }
36301         
36302         
36303         if (this.toolbar && this.toolbar.xtype) {
36304             this.toolbar.container = this.getView().getHeaderPanel(true);
36305             this.toolbar = new Roo.Toolbar(this.toolbar);
36306         }
36307         if (this.footer && this.footer.xtype) {
36308             this.footer.dataSource = this.getDataSource();
36309             this.footer.container = this.getView().getFooterPanel(true);
36310             this.footer = Roo.factory(this.footer, Roo);
36311         }
36312         if (this.dropTarget && this.dropTarget.xtype) {
36313             delete this.dropTarget.xtype;
36314             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36315         }
36316         
36317         
36318         this.rendered = true;
36319         this.fireEvent('render', this);
36320         return this;
36321     },
36322
36323         /**
36324          * Reconfigures the grid to use a different Store and Column Model.
36325          * The View will be bound to the new objects and refreshed.
36326          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36327          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36328          */
36329     reconfigure : function(dataSource, colModel){
36330         if(this.loadMask){
36331             this.loadMask.destroy();
36332             this.loadMask = new Roo.LoadMask(this.container,
36333                     Roo.apply({store:dataSource}, this.loadMask));
36334         }
36335         this.view.bind(dataSource, colModel);
36336         this.dataSource = dataSource;
36337         this.colModel = colModel;
36338         this.view.refresh(true);
36339     },
36340
36341     // private
36342     onKeyDown : function(e){
36343         this.fireEvent("keydown", e);
36344     },
36345
36346     /**
36347      * Destroy this grid.
36348      * @param {Boolean} removeEl True to remove the element
36349      */
36350     destroy : function(removeEl, keepListeners){
36351         if(this.loadMask){
36352             this.loadMask.destroy();
36353         }
36354         var c = this.container;
36355         c.removeAllListeners();
36356         this.view.destroy();
36357         this.colModel.purgeListeners();
36358         if(!keepListeners){
36359             this.purgeListeners();
36360         }
36361         c.update("");
36362         if(removeEl === true){
36363             c.remove();
36364         }
36365     },
36366
36367     // private
36368     processEvent : function(name, e){
36369         // does this fire select???
36370         //Roo.log('grid:processEvent '  + name);
36371         
36372         if (name != 'touchstart' ) {
36373             this.fireEvent(name, e);    
36374         }
36375         
36376         var t = e.getTarget();
36377         var v = this.view;
36378         var header = v.findHeaderIndex(t);
36379         if(header !== false){
36380             var ename = name == 'touchstart' ? 'click' : name;
36381              
36382             this.fireEvent("header" + ename, this, header, e);
36383         }else{
36384             var row = v.findRowIndex(t);
36385             var cell = v.findCellIndex(t);
36386             if (name == 'touchstart') {
36387                 // first touch is always a click.
36388                 // hopefull this happens after selection is updated.?
36389                 name = false;
36390                 
36391                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36392                     var cs = this.selModel.getSelectedCell();
36393                     if (row == cs[0] && cell == cs[1]){
36394                         name = 'dblclick';
36395                     }
36396                 }
36397                 if (typeof(this.selModel.getSelections) != 'undefined') {
36398                     var cs = this.selModel.getSelections();
36399                     var ds = this.dataSource;
36400                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36401                         name = 'dblclick';
36402                     }
36403                 }
36404                 if (!name) {
36405                     return;
36406                 }
36407             }
36408             
36409             
36410             if(row !== false){
36411                 this.fireEvent("row" + name, this, row, e);
36412                 if(cell !== false){
36413                     this.fireEvent("cell" + name, this, row, cell, e);
36414                 }
36415             }
36416         }
36417     },
36418
36419     // private
36420     onClick : function(e){
36421         this.processEvent("click", e);
36422     },
36423    // private
36424     onTouchStart : function(e){
36425         this.processEvent("touchstart", e);
36426     },
36427
36428     // private
36429     onContextMenu : function(e, t){
36430         this.processEvent("contextmenu", e);
36431     },
36432
36433     // private
36434     onDblClick : function(e){
36435         this.processEvent("dblclick", e);
36436     },
36437
36438     // private
36439     walkCells : function(row, col, step, fn, scope){
36440         var cm = this.colModel, clen = cm.getColumnCount();
36441         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36442         if(step < 0){
36443             if(col < 0){
36444                 row--;
36445                 first = false;
36446             }
36447             while(row >= 0){
36448                 if(!first){
36449                     col = clen-1;
36450                 }
36451                 first = false;
36452                 while(col >= 0){
36453                     if(fn.call(scope || this, row, col, cm) === true){
36454                         return [row, col];
36455                     }
36456                     col--;
36457                 }
36458                 row--;
36459             }
36460         } else {
36461             if(col >= clen){
36462                 row++;
36463                 first = false;
36464             }
36465             while(row < rlen){
36466                 if(!first){
36467                     col = 0;
36468                 }
36469                 first = false;
36470                 while(col < clen){
36471                     if(fn.call(scope || this, row, col, cm) === true){
36472                         return [row, col];
36473                     }
36474                     col++;
36475                 }
36476                 row++;
36477             }
36478         }
36479         return null;
36480     },
36481
36482     // private
36483     getSelections : function(){
36484         return this.selModel.getSelections();
36485     },
36486
36487     /**
36488      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36489      * but if manual update is required this method will initiate it.
36490      */
36491     autoSize : function(){
36492         if(this.rendered){
36493             this.view.layout();
36494             if(this.view.adjustForScroll){
36495                 this.view.adjustForScroll();
36496             }
36497         }
36498     },
36499
36500     /**
36501      * Returns the grid's underlying element.
36502      * @return {Element} The element
36503      */
36504     getGridEl : function(){
36505         return this.container;
36506     },
36507
36508     // private for compatibility, overridden by editor grid
36509     stopEditing : function(){},
36510
36511     /**
36512      * Returns the grid's SelectionModel.
36513      * @return {SelectionModel}
36514      */
36515     getSelectionModel : function(){
36516         if(!this.selModel){
36517             this.selModel = new Roo.grid.RowSelectionModel();
36518         }
36519         return this.selModel;
36520     },
36521
36522     /**
36523      * Returns the grid's DataSource.
36524      * @return {DataSource}
36525      */
36526     getDataSource : function(){
36527         return this.dataSource;
36528     },
36529
36530     /**
36531      * Returns the grid's ColumnModel.
36532      * @return {ColumnModel}
36533      */
36534     getColumnModel : function(){
36535         return this.colModel;
36536     },
36537
36538     /**
36539      * Returns the grid's GridView object.
36540      * @return {GridView}
36541      */
36542     getView : function(){
36543         if(!this.view){
36544             this.view = new Roo.grid.GridView(this.viewConfig);
36545         }
36546         return this.view;
36547     },
36548     /**
36549      * Called to get grid's drag proxy text, by default returns this.ddText.
36550      * @return {String}
36551      */
36552     getDragDropText : function(){
36553         var count = this.selModel.getCount();
36554         return String.format(this.ddText, count, count == 1 ? '' : 's');
36555     }
36556 });
36557 /**
36558  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36559  * %0 is replaced with the number of selected rows.
36560  * @type String
36561  */
36562 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36563  * Based on:
36564  * Ext JS Library 1.1.1
36565  * Copyright(c) 2006-2007, Ext JS, LLC.
36566  *
36567  * Originally Released Under LGPL - original licence link has changed is not relivant.
36568  *
36569  * Fork - LGPL
36570  * <script type="text/javascript">
36571  */
36572  
36573 Roo.grid.AbstractGridView = function(){
36574         this.grid = null;
36575         
36576         this.events = {
36577             "beforerowremoved" : true,
36578             "beforerowsinserted" : true,
36579             "beforerefresh" : true,
36580             "rowremoved" : true,
36581             "rowsinserted" : true,
36582             "rowupdated" : true,
36583             "refresh" : true
36584         };
36585     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36586 };
36587
36588 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36589     rowClass : "x-grid-row",
36590     cellClass : "x-grid-cell",
36591     tdClass : "x-grid-td",
36592     hdClass : "x-grid-hd",
36593     splitClass : "x-grid-hd-split",
36594     
36595     init: function(grid){
36596         this.grid = grid;
36597                 var cid = this.grid.getGridEl().id;
36598         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36599         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36600         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36601         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36602         },
36603         
36604     getColumnRenderers : function(){
36605         var renderers = [];
36606         var cm = this.grid.colModel;
36607         var colCount = cm.getColumnCount();
36608         for(var i = 0; i < colCount; i++){
36609             renderers[i] = cm.getRenderer(i);
36610         }
36611         return renderers;
36612     },
36613     
36614     getColumnIds : function(){
36615         var ids = [];
36616         var cm = this.grid.colModel;
36617         var colCount = cm.getColumnCount();
36618         for(var i = 0; i < colCount; i++){
36619             ids[i] = cm.getColumnId(i);
36620         }
36621         return ids;
36622     },
36623     
36624     getDataIndexes : function(){
36625         if(!this.indexMap){
36626             this.indexMap = this.buildIndexMap();
36627         }
36628         return this.indexMap.colToData;
36629     },
36630     
36631     getColumnIndexByDataIndex : function(dataIndex){
36632         if(!this.indexMap){
36633             this.indexMap = this.buildIndexMap();
36634         }
36635         return this.indexMap.dataToCol[dataIndex];
36636     },
36637     
36638     /**
36639      * Set a css style for a column dynamically. 
36640      * @param {Number} colIndex The index of the column
36641      * @param {String} name The css property name
36642      * @param {String} value The css value
36643      */
36644     setCSSStyle : function(colIndex, name, value){
36645         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36646         Roo.util.CSS.updateRule(selector, name, value);
36647     },
36648     
36649     generateRules : function(cm){
36650         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36651         Roo.util.CSS.removeStyleSheet(rulesId);
36652         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36653             var cid = cm.getColumnId(i);
36654             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36655                          this.tdSelector, cid, " {\n}\n",
36656                          this.hdSelector, cid, " {\n}\n",
36657                          this.splitSelector, cid, " {\n}\n");
36658         }
36659         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36660     }
36661 });/*
36662  * Based on:
36663  * Ext JS Library 1.1.1
36664  * Copyright(c) 2006-2007, Ext JS, LLC.
36665  *
36666  * Originally Released Under LGPL - original licence link has changed is not relivant.
36667  *
36668  * Fork - LGPL
36669  * <script type="text/javascript">
36670  */
36671
36672 // private
36673 // This is a support class used internally by the Grid components
36674 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36675     this.grid = grid;
36676     this.view = grid.getView();
36677     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36678     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36679     if(hd2){
36680         this.setHandleElId(Roo.id(hd));
36681         this.setOuterHandleElId(Roo.id(hd2));
36682     }
36683     this.scroll = false;
36684 };
36685 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36686     maxDragWidth: 120,
36687     getDragData : function(e){
36688         var t = Roo.lib.Event.getTarget(e);
36689         var h = this.view.findHeaderCell(t);
36690         if(h){
36691             return {ddel: h.firstChild, header:h};
36692         }
36693         return false;
36694     },
36695
36696     onInitDrag : function(e){
36697         this.view.headersDisabled = true;
36698         var clone = this.dragData.ddel.cloneNode(true);
36699         clone.id = Roo.id();
36700         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36701         this.proxy.update(clone);
36702         return true;
36703     },
36704
36705     afterValidDrop : function(){
36706         var v = this.view;
36707         setTimeout(function(){
36708             v.headersDisabled = false;
36709         }, 50);
36710     },
36711
36712     afterInvalidDrop : function(){
36713         var v = this.view;
36714         setTimeout(function(){
36715             v.headersDisabled = false;
36716         }, 50);
36717     }
36718 });
36719 /*
36720  * Based on:
36721  * Ext JS Library 1.1.1
36722  * Copyright(c) 2006-2007, Ext JS, LLC.
36723  *
36724  * Originally Released Under LGPL - original licence link has changed is not relivant.
36725  *
36726  * Fork - LGPL
36727  * <script type="text/javascript">
36728  */
36729 // private
36730 // This is a support class used internally by the Grid components
36731 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36732     this.grid = grid;
36733     this.view = grid.getView();
36734     // split the proxies so they don't interfere with mouse events
36735     this.proxyTop = Roo.DomHelper.append(document.body, {
36736         cls:"col-move-top", html:"&#160;"
36737     }, true);
36738     this.proxyBottom = Roo.DomHelper.append(document.body, {
36739         cls:"col-move-bottom", html:"&#160;"
36740     }, true);
36741     this.proxyTop.hide = this.proxyBottom.hide = function(){
36742         this.setLeftTop(-100,-100);
36743         this.setStyle("visibility", "hidden");
36744     };
36745     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36746     // temporarily disabled
36747     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36748     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36749 };
36750 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36751     proxyOffsets : [-4, -9],
36752     fly: Roo.Element.fly,
36753
36754     getTargetFromEvent : function(e){
36755         var t = Roo.lib.Event.getTarget(e);
36756         var cindex = this.view.findCellIndex(t);
36757         if(cindex !== false){
36758             return this.view.getHeaderCell(cindex);
36759         }
36760         return null;
36761     },
36762
36763     nextVisible : function(h){
36764         var v = this.view, cm = this.grid.colModel;
36765         h = h.nextSibling;
36766         while(h){
36767             if(!cm.isHidden(v.getCellIndex(h))){
36768                 return h;
36769             }
36770             h = h.nextSibling;
36771         }
36772         return null;
36773     },
36774
36775     prevVisible : function(h){
36776         var v = this.view, cm = this.grid.colModel;
36777         h = h.prevSibling;
36778         while(h){
36779             if(!cm.isHidden(v.getCellIndex(h))){
36780                 return h;
36781             }
36782             h = h.prevSibling;
36783         }
36784         return null;
36785     },
36786
36787     positionIndicator : function(h, n, e){
36788         var x = Roo.lib.Event.getPageX(e);
36789         var r = Roo.lib.Dom.getRegion(n.firstChild);
36790         var px, pt, py = r.top + this.proxyOffsets[1];
36791         if((r.right - x) <= (r.right-r.left)/2){
36792             px = r.right+this.view.borderWidth;
36793             pt = "after";
36794         }else{
36795             px = r.left;
36796             pt = "before";
36797         }
36798         var oldIndex = this.view.getCellIndex(h);
36799         var newIndex = this.view.getCellIndex(n);
36800
36801         if(this.grid.colModel.isFixed(newIndex)){
36802             return false;
36803         }
36804
36805         var locked = this.grid.colModel.isLocked(newIndex);
36806
36807         if(pt == "after"){
36808             newIndex++;
36809         }
36810         if(oldIndex < newIndex){
36811             newIndex--;
36812         }
36813         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36814             return false;
36815         }
36816         px +=  this.proxyOffsets[0];
36817         this.proxyTop.setLeftTop(px, py);
36818         this.proxyTop.show();
36819         if(!this.bottomOffset){
36820             this.bottomOffset = this.view.mainHd.getHeight();
36821         }
36822         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36823         this.proxyBottom.show();
36824         return pt;
36825     },
36826
36827     onNodeEnter : function(n, dd, e, data){
36828         if(data.header != n){
36829             this.positionIndicator(data.header, n, e);
36830         }
36831     },
36832
36833     onNodeOver : function(n, dd, e, data){
36834         var result = false;
36835         if(data.header != n){
36836             result = this.positionIndicator(data.header, n, e);
36837         }
36838         if(!result){
36839             this.proxyTop.hide();
36840             this.proxyBottom.hide();
36841         }
36842         return result ? this.dropAllowed : this.dropNotAllowed;
36843     },
36844
36845     onNodeOut : function(n, dd, e, data){
36846         this.proxyTop.hide();
36847         this.proxyBottom.hide();
36848     },
36849
36850     onNodeDrop : function(n, dd, e, data){
36851         var h = data.header;
36852         if(h != n){
36853             var cm = this.grid.colModel;
36854             var x = Roo.lib.Event.getPageX(e);
36855             var r = Roo.lib.Dom.getRegion(n.firstChild);
36856             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36857             var oldIndex = this.view.getCellIndex(h);
36858             var newIndex = this.view.getCellIndex(n);
36859             var locked = cm.isLocked(newIndex);
36860             if(pt == "after"){
36861                 newIndex++;
36862             }
36863             if(oldIndex < newIndex){
36864                 newIndex--;
36865             }
36866             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36867                 return false;
36868             }
36869             cm.setLocked(oldIndex, locked, true);
36870             cm.moveColumn(oldIndex, newIndex);
36871             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36872             return true;
36873         }
36874         return false;
36875     }
36876 });
36877 /*
36878  * Based on:
36879  * Ext JS Library 1.1.1
36880  * Copyright(c) 2006-2007, Ext JS, LLC.
36881  *
36882  * Originally Released Under LGPL - original licence link has changed is not relivant.
36883  *
36884  * Fork - LGPL
36885  * <script type="text/javascript">
36886  */
36887   
36888 /**
36889  * @class Roo.grid.GridView
36890  * @extends Roo.util.Observable
36891  *
36892  * @constructor
36893  * @param {Object} config
36894  */
36895 Roo.grid.GridView = function(config){
36896     Roo.grid.GridView.superclass.constructor.call(this);
36897     this.el = null;
36898
36899     Roo.apply(this, config);
36900 };
36901
36902 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36903
36904     unselectable :  'unselectable="on"',
36905     unselectableCls :  'x-unselectable',
36906     
36907     
36908     rowClass : "x-grid-row",
36909
36910     cellClass : "x-grid-col",
36911
36912     tdClass : "x-grid-td",
36913
36914     hdClass : "x-grid-hd",
36915
36916     splitClass : "x-grid-split",
36917
36918     sortClasses : ["sort-asc", "sort-desc"],
36919
36920     enableMoveAnim : false,
36921
36922     hlColor: "C3DAF9",
36923
36924     dh : Roo.DomHelper,
36925
36926     fly : Roo.Element.fly,
36927
36928     css : Roo.util.CSS,
36929
36930     borderWidth: 1,
36931
36932     splitOffset: 3,
36933
36934     scrollIncrement : 22,
36935
36936     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36937
36938     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36939
36940     bind : function(ds, cm){
36941         if(this.ds){
36942             this.ds.un("load", this.onLoad, this);
36943             this.ds.un("datachanged", this.onDataChange, this);
36944             this.ds.un("add", this.onAdd, this);
36945             this.ds.un("remove", this.onRemove, this);
36946             this.ds.un("update", this.onUpdate, this);
36947             this.ds.un("clear", this.onClear, this);
36948         }
36949         if(ds){
36950             ds.on("load", this.onLoad, this);
36951             ds.on("datachanged", this.onDataChange, this);
36952             ds.on("add", this.onAdd, this);
36953             ds.on("remove", this.onRemove, this);
36954             ds.on("update", this.onUpdate, this);
36955             ds.on("clear", this.onClear, this);
36956         }
36957         this.ds = ds;
36958
36959         if(this.cm){
36960             this.cm.un("widthchange", this.onColWidthChange, this);
36961             this.cm.un("headerchange", this.onHeaderChange, this);
36962             this.cm.un("hiddenchange", this.onHiddenChange, this);
36963             this.cm.un("columnmoved", this.onColumnMove, this);
36964             this.cm.un("columnlockchange", this.onColumnLock, this);
36965         }
36966         if(cm){
36967             this.generateRules(cm);
36968             cm.on("widthchange", this.onColWidthChange, this);
36969             cm.on("headerchange", this.onHeaderChange, this);
36970             cm.on("hiddenchange", this.onHiddenChange, this);
36971             cm.on("columnmoved", this.onColumnMove, this);
36972             cm.on("columnlockchange", this.onColumnLock, this);
36973         }
36974         this.cm = cm;
36975     },
36976
36977     init: function(grid){
36978         Roo.grid.GridView.superclass.init.call(this, grid);
36979
36980         this.bind(grid.dataSource, grid.colModel);
36981
36982         grid.on("headerclick", this.handleHeaderClick, this);
36983
36984         if(grid.trackMouseOver){
36985             grid.on("mouseover", this.onRowOver, this);
36986             grid.on("mouseout", this.onRowOut, this);
36987         }
36988         grid.cancelTextSelection = function(){};
36989         this.gridId = grid.id;
36990
36991         var tpls = this.templates || {};
36992
36993         if(!tpls.master){
36994             tpls.master = new Roo.Template(
36995                '<div class="x-grid" hidefocus="true">',
36996                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36997                   '<div class="x-grid-topbar"></div>',
36998                   '<div class="x-grid-scroller"><div></div></div>',
36999                   '<div class="x-grid-locked">',
37000                       '<div class="x-grid-header">{lockedHeader}</div>',
37001                       '<div class="x-grid-body">{lockedBody}</div>',
37002                   "</div>",
37003                   '<div class="x-grid-viewport">',
37004                       '<div class="x-grid-header">{header}</div>',
37005                       '<div class="x-grid-body">{body}</div>',
37006                   "</div>",
37007                   '<div class="x-grid-bottombar"></div>',
37008                  
37009                   '<div class="x-grid-resize-proxy">&#160;</div>',
37010                "</div>"
37011             );
37012             tpls.master.disableformats = true;
37013         }
37014
37015         if(!tpls.header){
37016             tpls.header = new Roo.Template(
37017                '<table border="0" cellspacing="0" cellpadding="0">',
37018                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37019                "</table>{splits}"
37020             );
37021             tpls.header.disableformats = true;
37022         }
37023         tpls.header.compile();
37024
37025         if(!tpls.hcell){
37026             tpls.hcell = new Roo.Template(
37027                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37028                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37029                 "</div></td>"
37030              );
37031              tpls.hcell.disableFormats = true;
37032         }
37033         tpls.hcell.compile();
37034
37035         if(!tpls.hsplit){
37036             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37037                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37038             tpls.hsplit.disableFormats = true;
37039         }
37040         tpls.hsplit.compile();
37041
37042         if(!tpls.body){
37043             tpls.body = new Roo.Template(
37044                '<table border="0" cellspacing="0" cellpadding="0">',
37045                "<tbody>{rows}</tbody>",
37046                "</table>"
37047             );
37048             tpls.body.disableFormats = true;
37049         }
37050         tpls.body.compile();
37051
37052         if(!tpls.row){
37053             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37054             tpls.row.disableFormats = true;
37055         }
37056         tpls.row.compile();
37057
37058         if(!tpls.cell){
37059             tpls.cell = new Roo.Template(
37060                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37061                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37062                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37063                 "</td>"
37064             );
37065             tpls.cell.disableFormats = true;
37066         }
37067         tpls.cell.compile();
37068
37069         this.templates = tpls;
37070     },
37071
37072     // remap these for backwards compat
37073     onColWidthChange : function(){
37074         this.updateColumns.apply(this, arguments);
37075     },
37076     onHeaderChange : function(){
37077         this.updateHeaders.apply(this, arguments);
37078     }, 
37079     onHiddenChange : function(){
37080         this.handleHiddenChange.apply(this, arguments);
37081     },
37082     onColumnMove : function(){
37083         this.handleColumnMove.apply(this, arguments);
37084     },
37085     onColumnLock : function(){
37086         this.handleLockChange.apply(this, arguments);
37087     },
37088
37089     onDataChange : function(){
37090         this.refresh();
37091         this.updateHeaderSortState();
37092     },
37093
37094     onClear : function(){
37095         this.refresh();
37096     },
37097
37098     onUpdate : function(ds, record){
37099         this.refreshRow(record);
37100     },
37101
37102     refreshRow : function(record){
37103         var ds = this.ds, index;
37104         if(typeof record == 'number'){
37105             index = record;
37106             record = ds.getAt(index);
37107         }else{
37108             index = ds.indexOf(record);
37109         }
37110         this.insertRows(ds, index, index, true);
37111         this.onRemove(ds, record, index+1, true);
37112         this.syncRowHeights(index, index);
37113         this.layout();
37114         this.fireEvent("rowupdated", this, index, record);
37115     },
37116
37117     onAdd : function(ds, records, index){
37118         this.insertRows(ds, index, index + (records.length-1));
37119     },
37120
37121     onRemove : function(ds, record, index, isUpdate){
37122         if(isUpdate !== true){
37123             this.fireEvent("beforerowremoved", this, index, record);
37124         }
37125         var bt = this.getBodyTable(), lt = this.getLockedTable();
37126         if(bt.rows[index]){
37127             bt.firstChild.removeChild(bt.rows[index]);
37128         }
37129         if(lt.rows[index]){
37130             lt.firstChild.removeChild(lt.rows[index]);
37131         }
37132         if(isUpdate !== true){
37133             this.stripeRows(index);
37134             this.syncRowHeights(index, index);
37135             this.layout();
37136             this.fireEvent("rowremoved", this, index, record);
37137         }
37138     },
37139
37140     onLoad : function(){
37141         this.scrollToTop();
37142     },
37143
37144     /**
37145      * Scrolls the grid to the top
37146      */
37147     scrollToTop : function(){
37148         if(this.scroller){
37149             this.scroller.dom.scrollTop = 0;
37150             this.syncScroll();
37151         }
37152     },
37153
37154     /**
37155      * Gets a panel in the header of the grid that can be used for toolbars etc.
37156      * After modifying the contents of this panel a call to grid.autoSize() may be
37157      * required to register any changes in size.
37158      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37159      * @return Roo.Element
37160      */
37161     getHeaderPanel : function(doShow){
37162         if(doShow){
37163             this.headerPanel.show();
37164         }
37165         return this.headerPanel;
37166     },
37167
37168     /**
37169      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37170      * After modifying the contents of this panel a call to grid.autoSize() may be
37171      * required to register any changes in size.
37172      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37173      * @return Roo.Element
37174      */
37175     getFooterPanel : function(doShow){
37176         if(doShow){
37177             this.footerPanel.show();
37178         }
37179         return this.footerPanel;
37180     },
37181
37182     initElements : function(){
37183         var E = Roo.Element;
37184         var el = this.grid.getGridEl().dom.firstChild;
37185         var cs = el.childNodes;
37186
37187         this.el = new E(el);
37188         
37189          this.focusEl = new E(el.firstChild);
37190         this.focusEl.swallowEvent("click", true);
37191         
37192         this.headerPanel = new E(cs[1]);
37193         this.headerPanel.enableDisplayMode("block");
37194
37195         this.scroller = new E(cs[2]);
37196         this.scrollSizer = new E(this.scroller.dom.firstChild);
37197
37198         this.lockedWrap = new E(cs[3]);
37199         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37200         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37201
37202         this.mainWrap = new E(cs[4]);
37203         this.mainHd = new E(this.mainWrap.dom.firstChild);
37204         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37205
37206         this.footerPanel = new E(cs[5]);
37207         this.footerPanel.enableDisplayMode("block");
37208
37209         this.resizeProxy = new E(cs[6]);
37210
37211         this.headerSelector = String.format(
37212            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37213            this.lockedHd.id, this.mainHd.id
37214         );
37215
37216         this.splitterSelector = String.format(
37217            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37218            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37219         );
37220     },
37221     idToCssName : function(s)
37222     {
37223         return s.replace(/[^a-z0-9]+/ig, '-');
37224     },
37225
37226     getHeaderCell : function(index){
37227         return Roo.DomQuery.select(this.headerSelector)[index];
37228     },
37229
37230     getHeaderCellMeasure : function(index){
37231         return this.getHeaderCell(index).firstChild;
37232     },
37233
37234     getHeaderCellText : function(index){
37235         return this.getHeaderCell(index).firstChild.firstChild;
37236     },
37237
37238     getLockedTable : function(){
37239         return this.lockedBody.dom.firstChild;
37240     },
37241
37242     getBodyTable : function(){
37243         return this.mainBody.dom.firstChild;
37244     },
37245
37246     getLockedRow : function(index){
37247         return this.getLockedTable().rows[index];
37248     },
37249
37250     getRow : function(index){
37251         return this.getBodyTable().rows[index];
37252     },
37253
37254     getRowComposite : function(index){
37255         if(!this.rowEl){
37256             this.rowEl = new Roo.CompositeElementLite();
37257         }
37258         var els = [], lrow, mrow;
37259         if(lrow = this.getLockedRow(index)){
37260             els.push(lrow);
37261         }
37262         if(mrow = this.getRow(index)){
37263             els.push(mrow);
37264         }
37265         this.rowEl.elements = els;
37266         return this.rowEl;
37267     },
37268     /**
37269      * Gets the 'td' of the cell
37270      * 
37271      * @param {Integer} rowIndex row to select
37272      * @param {Integer} colIndex column to select
37273      * 
37274      * @return {Object} 
37275      */
37276     getCell : function(rowIndex, colIndex){
37277         var locked = this.cm.getLockedCount();
37278         var source;
37279         if(colIndex < locked){
37280             source = this.lockedBody.dom.firstChild;
37281         }else{
37282             source = this.mainBody.dom.firstChild;
37283             colIndex -= locked;
37284         }
37285         return source.rows[rowIndex].childNodes[colIndex];
37286     },
37287
37288     getCellText : function(rowIndex, colIndex){
37289         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37290     },
37291
37292     getCellBox : function(cell){
37293         var b = this.fly(cell).getBox();
37294         if(Roo.isOpera){ // opera fails to report the Y
37295             b.y = cell.offsetTop + this.mainBody.getY();
37296         }
37297         return b;
37298     },
37299
37300     getCellIndex : function(cell){
37301         var id = String(cell.className).match(this.cellRE);
37302         if(id){
37303             return parseInt(id[1], 10);
37304         }
37305         return 0;
37306     },
37307
37308     findHeaderIndex : function(n){
37309         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37310         return r ? this.getCellIndex(r) : false;
37311     },
37312
37313     findHeaderCell : function(n){
37314         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37315         return r ? r : false;
37316     },
37317
37318     findRowIndex : function(n){
37319         if(!n){
37320             return false;
37321         }
37322         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37323         return r ? r.rowIndex : false;
37324     },
37325
37326     findCellIndex : function(node){
37327         var stop = this.el.dom;
37328         while(node && node != stop){
37329             if(this.findRE.test(node.className)){
37330                 return this.getCellIndex(node);
37331             }
37332             node = node.parentNode;
37333         }
37334         return false;
37335     },
37336
37337     getColumnId : function(index){
37338         return this.cm.getColumnId(index);
37339     },
37340
37341     getSplitters : function()
37342     {
37343         if(this.splitterSelector){
37344            return Roo.DomQuery.select(this.splitterSelector);
37345         }else{
37346             return null;
37347       }
37348     },
37349
37350     getSplitter : function(index){
37351         return this.getSplitters()[index];
37352     },
37353
37354     onRowOver : function(e, t){
37355         var row;
37356         if((row = this.findRowIndex(t)) !== false){
37357             this.getRowComposite(row).addClass("x-grid-row-over");
37358         }
37359     },
37360
37361     onRowOut : function(e, t){
37362         var row;
37363         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37364             this.getRowComposite(row).removeClass("x-grid-row-over");
37365         }
37366     },
37367
37368     renderHeaders : function(){
37369         var cm = this.cm;
37370         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37371         var cb = [], lb = [], sb = [], lsb = [], p = {};
37372         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37373             p.cellId = "x-grid-hd-0-" + i;
37374             p.splitId = "x-grid-csplit-0-" + i;
37375             p.id = cm.getColumnId(i);
37376             p.title = cm.getColumnTooltip(i) || "";
37377             p.value = cm.getColumnHeader(i) || "";
37378             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37379             if(!cm.isLocked(i)){
37380                 cb[cb.length] = ct.apply(p);
37381                 sb[sb.length] = st.apply(p);
37382             }else{
37383                 lb[lb.length] = ct.apply(p);
37384                 lsb[lsb.length] = st.apply(p);
37385             }
37386         }
37387         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37388                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37389     },
37390
37391     updateHeaders : function(){
37392         var html = this.renderHeaders();
37393         this.lockedHd.update(html[0]);
37394         this.mainHd.update(html[1]);
37395     },
37396
37397     /**
37398      * Focuses the specified row.
37399      * @param {Number} row The row index
37400      */
37401     focusRow : function(row)
37402     {
37403         //Roo.log('GridView.focusRow');
37404         var x = this.scroller.dom.scrollLeft;
37405         this.focusCell(row, 0, false);
37406         this.scroller.dom.scrollLeft = x;
37407     },
37408
37409     /**
37410      * Focuses the specified cell.
37411      * @param {Number} row The row index
37412      * @param {Number} col The column index
37413      * @param {Boolean} hscroll false to disable horizontal scrolling
37414      */
37415     focusCell : function(row, col, hscroll)
37416     {
37417         //Roo.log('GridView.focusCell');
37418         var el = this.ensureVisible(row, col, hscroll);
37419         this.focusEl.alignTo(el, "tl-tl");
37420         if(Roo.isGecko){
37421             this.focusEl.focus();
37422         }else{
37423             this.focusEl.focus.defer(1, this.focusEl);
37424         }
37425     },
37426
37427     /**
37428      * Scrolls the specified cell into view
37429      * @param {Number} row The row index
37430      * @param {Number} col The column index
37431      * @param {Boolean} hscroll false to disable horizontal scrolling
37432      */
37433     ensureVisible : function(row, col, hscroll)
37434     {
37435         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37436         //return null; //disable for testing.
37437         if(typeof row != "number"){
37438             row = row.rowIndex;
37439         }
37440         if(row < 0 && row >= this.ds.getCount()){
37441             return  null;
37442         }
37443         col = (col !== undefined ? col : 0);
37444         var cm = this.grid.colModel;
37445         while(cm.isHidden(col)){
37446             col++;
37447         }
37448
37449         var el = this.getCell(row, col);
37450         if(!el){
37451             return null;
37452         }
37453         var c = this.scroller.dom;
37454
37455         var ctop = parseInt(el.offsetTop, 10);
37456         var cleft = parseInt(el.offsetLeft, 10);
37457         var cbot = ctop + el.offsetHeight;
37458         var cright = cleft + el.offsetWidth;
37459         
37460         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37461         var stop = parseInt(c.scrollTop, 10);
37462         var sleft = parseInt(c.scrollLeft, 10);
37463         var sbot = stop + ch;
37464         var sright = sleft + c.clientWidth;
37465         /*
37466         Roo.log('GridView.ensureVisible:' +
37467                 ' ctop:' + ctop +
37468                 ' c.clientHeight:' + c.clientHeight +
37469                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37470                 ' stop:' + stop +
37471                 ' cbot:' + cbot +
37472                 ' sbot:' + sbot +
37473                 ' ch:' + ch  
37474                 );
37475         */
37476         if(ctop < stop){
37477              c.scrollTop = ctop;
37478             //Roo.log("set scrolltop to ctop DISABLE?");
37479         }else if(cbot > sbot){
37480             //Roo.log("set scrolltop to cbot-ch");
37481             c.scrollTop = cbot-ch;
37482         }
37483         
37484         if(hscroll !== false){
37485             if(cleft < sleft){
37486                 c.scrollLeft = cleft;
37487             }else if(cright > sright){
37488                 c.scrollLeft = cright-c.clientWidth;
37489             }
37490         }
37491          
37492         return el;
37493     },
37494
37495     updateColumns : function(){
37496         this.grid.stopEditing();
37497         var cm = this.grid.colModel, colIds = this.getColumnIds();
37498         //var totalWidth = cm.getTotalWidth();
37499         var pos = 0;
37500         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37501             //if(cm.isHidden(i)) continue;
37502             var w = cm.getColumnWidth(i);
37503             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37504             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37505         }
37506         this.updateSplitters();
37507     },
37508
37509     generateRules : function(cm){
37510         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37511         Roo.util.CSS.removeStyleSheet(rulesId);
37512         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37513             var cid = cm.getColumnId(i);
37514             var align = '';
37515             if(cm.config[i].align){
37516                 align = 'text-align:'+cm.config[i].align+';';
37517             }
37518             var hidden = '';
37519             if(cm.isHidden(i)){
37520                 hidden = 'display:none;';
37521             }
37522             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37523             ruleBuf.push(
37524                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37525                     this.hdSelector, cid, " {\n", align, width, "}\n",
37526                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37527                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37528         }
37529         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37530     },
37531
37532     updateSplitters : function(){
37533         var cm = this.cm, s = this.getSplitters();
37534         if(s){ // splitters not created yet
37535             var pos = 0, locked = true;
37536             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37537                 if(cm.isHidden(i)) continue;
37538                 var w = cm.getColumnWidth(i); // make sure it's a number
37539                 if(!cm.isLocked(i) && locked){
37540                     pos = 0;
37541                     locked = false;
37542                 }
37543                 pos += w;
37544                 s[i].style.left = (pos-this.splitOffset) + "px";
37545             }
37546         }
37547     },
37548
37549     handleHiddenChange : function(colModel, colIndex, hidden){
37550         if(hidden){
37551             this.hideColumn(colIndex);
37552         }else{
37553             this.unhideColumn(colIndex);
37554         }
37555     },
37556
37557     hideColumn : function(colIndex){
37558         var cid = this.getColumnId(colIndex);
37559         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37560         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37561         if(Roo.isSafari){
37562             this.updateHeaders();
37563         }
37564         this.updateSplitters();
37565         this.layout();
37566     },
37567
37568     unhideColumn : function(colIndex){
37569         var cid = this.getColumnId(colIndex);
37570         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37571         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37572
37573         if(Roo.isSafari){
37574             this.updateHeaders();
37575         }
37576         this.updateSplitters();
37577         this.layout();
37578     },
37579
37580     insertRows : function(dm, firstRow, lastRow, isUpdate){
37581         if(firstRow == 0 && lastRow == dm.getCount()-1){
37582             this.refresh();
37583         }else{
37584             if(!isUpdate){
37585                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37586             }
37587             var s = this.getScrollState();
37588             var markup = this.renderRows(firstRow, lastRow);
37589             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37590             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37591             this.restoreScroll(s);
37592             if(!isUpdate){
37593                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37594                 this.syncRowHeights(firstRow, lastRow);
37595                 this.stripeRows(firstRow);
37596                 this.layout();
37597             }
37598         }
37599     },
37600
37601     bufferRows : function(markup, target, index){
37602         var before = null, trows = target.rows, tbody = target.tBodies[0];
37603         if(index < trows.length){
37604             before = trows[index];
37605         }
37606         var b = document.createElement("div");
37607         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37608         var rows = b.firstChild.rows;
37609         for(var i = 0, len = rows.length; i < len; i++){
37610             if(before){
37611                 tbody.insertBefore(rows[0], before);
37612             }else{
37613                 tbody.appendChild(rows[0]);
37614             }
37615         }
37616         b.innerHTML = "";
37617         b = null;
37618     },
37619
37620     deleteRows : function(dm, firstRow, lastRow){
37621         if(dm.getRowCount()<1){
37622             this.fireEvent("beforerefresh", this);
37623             this.mainBody.update("");
37624             this.lockedBody.update("");
37625             this.fireEvent("refresh", this);
37626         }else{
37627             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37628             var bt = this.getBodyTable();
37629             var tbody = bt.firstChild;
37630             var rows = bt.rows;
37631             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37632                 tbody.removeChild(rows[firstRow]);
37633             }
37634             this.stripeRows(firstRow);
37635             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37636         }
37637     },
37638
37639     updateRows : function(dataSource, firstRow, lastRow){
37640         var s = this.getScrollState();
37641         this.refresh();
37642         this.restoreScroll(s);
37643     },
37644
37645     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37646         if(!noRefresh){
37647            this.refresh();
37648         }
37649         this.updateHeaderSortState();
37650     },
37651
37652     getScrollState : function(){
37653         
37654         var sb = this.scroller.dom;
37655         return {left: sb.scrollLeft, top: sb.scrollTop};
37656     },
37657
37658     stripeRows : function(startRow){
37659         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37660             return;
37661         }
37662         startRow = startRow || 0;
37663         var rows = this.getBodyTable().rows;
37664         var lrows = this.getLockedTable().rows;
37665         var cls = ' x-grid-row-alt ';
37666         for(var i = startRow, len = rows.length; i < len; i++){
37667             var row = rows[i], lrow = lrows[i];
37668             var isAlt = ((i+1) % 2 == 0);
37669             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37670             if(isAlt == hasAlt){
37671                 continue;
37672             }
37673             if(isAlt){
37674                 row.className += " x-grid-row-alt";
37675             }else{
37676                 row.className = row.className.replace("x-grid-row-alt", "");
37677             }
37678             if(lrow){
37679                 lrow.className = row.className;
37680             }
37681         }
37682     },
37683
37684     restoreScroll : function(state){
37685         //Roo.log('GridView.restoreScroll');
37686         var sb = this.scroller.dom;
37687         sb.scrollLeft = state.left;
37688         sb.scrollTop = state.top;
37689         this.syncScroll();
37690     },
37691
37692     syncScroll : function(){
37693         //Roo.log('GridView.syncScroll');
37694         var sb = this.scroller.dom;
37695         var sh = this.mainHd.dom;
37696         var bs = this.mainBody.dom;
37697         var lv = this.lockedBody.dom;
37698         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37699         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37700     },
37701
37702     handleScroll : function(e){
37703         this.syncScroll();
37704         var sb = this.scroller.dom;
37705         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37706         e.stopEvent();
37707     },
37708
37709     handleWheel : function(e){
37710         var d = e.getWheelDelta();
37711         this.scroller.dom.scrollTop -= d*22;
37712         // set this here to prevent jumpy scrolling on large tables
37713         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37714         e.stopEvent();
37715     },
37716
37717     renderRows : function(startRow, endRow){
37718         // pull in all the crap needed to render rows
37719         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37720         var colCount = cm.getColumnCount();
37721
37722         if(ds.getCount() < 1){
37723             return ["", ""];
37724         }
37725
37726         // build a map for all the columns
37727         var cs = [];
37728         for(var i = 0; i < colCount; i++){
37729             var name = cm.getDataIndex(i);
37730             cs[i] = {
37731                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37732                 renderer : cm.getRenderer(i),
37733                 id : cm.getColumnId(i),
37734                 locked : cm.isLocked(i)
37735             };
37736         }
37737
37738         startRow = startRow || 0;
37739         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37740
37741         // records to render
37742         var rs = ds.getRange(startRow, endRow);
37743
37744         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37745     },
37746
37747     // As much as I hate to duplicate code, this was branched because FireFox really hates
37748     // [].join("") on strings. The performance difference was substantial enough to
37749     // branch this function
37750     doRender : Roo.isGecko ?
37751             function(cs, rs, ds, startRow, colCount, stripe){
37752                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37753                 // buffers
37754                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37755                 
37756                 var hasListener = this.grid.hasListener('rowclass');
37757                 var rowcfg = {};
37758                 for(var j = 0, len = rs.length; j < len; j++){
37759                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37760                     for(var i = 0; i < colCount; i++){
37761                         c = cs[i];
37762                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37763                         p.id = c.id;
37764                         p.css = p.attr = "";
37765                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37766                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37767                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37768                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37769                         }
37770                         var markup = ct.apply(p);
37771                         if(!c.locked){
37772                             cb+= markup;
37773                         }else{
37774                             lcb+= markup;
37775                         }
37776                     }
37777                     var alt = [];
37778                     if(stripe && ((rowIndex+1) % 2 == 0)){
37779                         alt.push("x-grid-row-alt")
37780                     }
37781                     if(r.dirty){
37782                         alt.push(  " x-grid-dirty-row");
37783                     }
37784                     rp.cells = lcb;
37785                     if(this.getRowClass){
37786                         alt.push(this.getRowClass(r, rowIndex));
37787                     }
37788                     if (hasListener) {
37789                         rowcfg = {
37790                              
37791                             record: r,
37792                             rowIndex : rowIndex,
37793                             rowClass : ''
37794                         }
37795                         this.grid.fireEvent('rowclass', this, rowcfg);
37796                         alt.push(rowcfg.rowClass);
37797                     }
37798                     rp.alt = alt.join(" ");
37799                     lbuf+= rt.apply(rp);
37800                     rp.cells = cb;
37801                     buf+=  rt.apply(rp);
37802                 }
37803                 return [lbuf, buf];
37804             } :
37805             function(cs, rs, ds, startRow, colCount, stripe){
37806                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37807                 // buffers
37808                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37809                 var hasListener = this.grid.hasListener('rowclass');
37810  
37811                 var rowcfg = {};
37812                 for(var j = 0, len = rs.length; j < len; j++){
37813                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37814                     for(var i = 0; i < colCount; i++){
37815                         c = cs[i];
37816                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37817                         p.id = c.id;
37818                         p.css = p.attr = "";
37819                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37820                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37821                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37822                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37823                         }
37824                         
37825                         var markup = ct.apply(p);
37826                         if(!c.locked){
37827                             cb[cb.length] = markup;
37828                         }else{
37829                             lcb[lcb.length] = markup;
37830                         }
37831                     }
37832                     var alt = [];
37833                     if(stripe && ((rowIndex+1) % 2 == 0)){
37834                         alt.push( "x-grid-row-alt");
37835                     }
37836                     if(r.dirty){
37837                         alt.push(" x-grid-dirty-row");
37838                     }
37839                     rp.cells = lcb;
37840                     if(this.getRowClass){
37841                         alt.push( this.getRowClass(r, rowIndex));
37842                     }
37843                     if (hasListener) {
37844                         rowcfg = {
37845                              
37846                             record: r,
37847                             rowIndex : rowIndex,
37848                             rowClass : ''
37849                         }
37850                         this.grid.fireEvent('rowclass', this, rowcfg);
37851                         alt.push(rowcfg.rowClass);
37852                     }
37853                     rp.alt = alt.join(" ");
37854                     rp.cells = lcb.join("");
37855                     lbuf[lbuf.length] = rt.apply(rp);
37856                     rp.cells = cb.join("");
37857                     buf[buf.length] =  rt.apply(rp);
37858                 }
37859                 return [lbuf.join(""), buf.join("")];
37860             },
37861
37862     renderBody : function(){
37863         var markup = this.renderRows();
37864         var bt = this.templates.body;
37865         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37866     },
37867
37868     /**
37869      * Refreshes the grid
37870      * @param {Boolean} headersToo
37871      */
37872     refresh : function(headersToo){
37873         this.fireEvent("beforerefresh", this);
37874         this.grid.stopEditing();
37875         var result = this.renderBody();
37876         this.lockedBody.update(result[0]);
37877         this.mainBody.update(result[1]);
37878         if(headersToo === true){
37879             this.updateHeaders();
37880             this.updateColumns();
37881             this.updateSplitters();
37882             this.updateHeaderSortState();
37883         }
37884         this.syncRowHeights();
37885         this.layout();
37886         this.fireEvent("refresh", this);
37887     },
37888
37889     handleColumnMove : function(cm, oldIndex, newIndex){
37890         this.indexMap = null;
37891         var s = this.getScrollState();
37892         this.refresh(true);
37893         this.restoreScroll(s);
37894         this.afterMove(newIndex);
37895     },
37896
37897     afterMove : function(colIndex){
37898         if(this.enableMoveAnim && Roo.enableFx){
37899             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37900         }
37901         // if multisort - fix sortOrder, and reload..
37902         if (this.grid.dataSource.multiSort) {
37903             // the we can call sort again..
37904             var dm = this.grid.dataSource;
37905             var cm = this.grid.colModel;
37906             var so = [];
37907             for(var i = 0; i < cm.config.length; i++ ) {
37908                 
37909                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37910                     continue; // dont' bother, it's not in sort list or being set.
37911                 }
37912                 
37913                 so.push(cm.config[i].dataIndex);
37914             };
37915             dm.sortOrder = so;
37916             dm.load(dm.lastOptions);
37917             
37918             
37919         }
37920         
37921     },
37922
37923     updateCell : function(dm, rowIndex, dataIndex){
37924         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37925         if(typeof colIndex == "undefined"){ // not present in grid
37926             return;
37927         }
37928         var cm = this.grid.colModel;
37929         var cell = this.getCell(rowIndex, colIndex);
37930         var cellText = this.getCellText(rowIndex, colIndex);
37931
37932         var p = {
37933             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37934             id : cm.getColumnId(colIndex),
37935             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37936         };
37937         var renderer = cm.getRenderer(colIndex);
37938         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37939         if(typeof val == "undefined" || val === "") val = "&#160;";
37940         cellText.innerHTML = val;
37941         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37942         this.syncRowHeights(rowIndex, rowIndex);
37943     },
37944
37945     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37946         var maxWidth = 0;
37947         if(this.grid.autoSizeHeaders){
37948             var h = this.getHeaderCellMeasure(colIndex);
37949             maxWidth = Math.max(maxWidth, h.scrollWidth);
37950         }
37951         var tb, index;
37952         if(this.cm.isLocked(colIndex)){
37953             tb = this.getLockedTable();
37954             index = colIndex;
37955         }else{
37956             tb = this.getBodyTable();
37957             index = colIndex - this.cm.getLockedCount();
37958         }
37959         if(tb && tb.rows){
37960             var rows = tb.rows;
37961             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37962             for(var i = 0; i < stopIndex; i++){
37963                 var cell = rows[i].childNodes[index].firstChild;
37964                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37965             }
37966         }
37967         return maxWidth + /*margin for error in IE*/ 5;
37968     },
37969     /**
37970      * Autofit a column to its content.
37971      * @param {Number} colIndex
37972      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37973      */
37974      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37975          if(this.cm.isHidden(colIndex)){
37976              return; // can't calc a hidden column
37977          }
37978         if(forceMinSize){
37979             var cid = this.cm.getColumnId(colIndex);
37980             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37981            if(this.grid.autoSizeHeaders){
37982                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37983            }
37984         }
37985         var newWidth = this.calcColumnWidth(colIndex);
37986         this.cm.setColumnWidth(colIndex,
37987             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37988         if(!suppressEvent){
37989             this.grid.fireEvent("columnresize", colIndex, newWidth);
37990         }
37991     },
37992
37993     /**
37994      * Autofits all columns to their content and then expands to fit any extra space in the grid
37995      */
37996      autoSizeColumns : function(){
37997         var cm = this.grid.colModel;
37998         var colCount = cm.getColumnCount();
37999         for(var i = 0; i < colCount; i++){
38000             this.autoSizeColumn(i, true, true);
38001         }
38002         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38003             this.fitColumns();
38004         }else{
38005             this.updateColumns();
38006             this.layout();
38007         }
38008     },
38009
38010     /**
38011      * Autofits all columns to the grid's width proportionate with their current size
38012      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38013      */
38014     fitColumns : function(reserveScrollSpace){
38015         var cm = this.grid.colModel;
38016         var colCount = cm.getColumnCount();
38017         var cols = [];
38018         var width = 0;
38019         var i, w;
38020         for (i = 0; i < colCount; i++){
38021             if(!cm.isHidden(i) && !cm.isFixed(i)){
38022                 w = cm.getColumnWidth(i);
38023                 cols.push(i);
38024                 cols.push(w);
38025                 width += w;
38026             }
38027         }
38028         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38029         if(reserveScrollSpace){
38030             avail -= 17;
38031         }
38032         var frac = (avail - cm.getTotalWidth())/width;
38033         while (cols.length){
38034             w = cols.pop();
38035             i = cols.pop();
38036             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38037         }
38038         this.updateColumns();
38039         this.layout();
38040     },
38041
38042     onRowSelect : function(rowIndex){
38043         var row = this.getRowComposite(rowIndex);
38044         row.addClass("x-grid-row-selected");
38045     },
38046
38047     onRowDeselect : function(rowIndex){
38048         var row = this.getRowComposite(rowIndex);
38049         row.removeClass("x-grid-row-selected");
38050     },
38051
38052     onCellSelect : function(row, col){
38053         var cell = this.getCell(row, col);
38054         if(cell){
38055             Roo.fly(cell).addClass("x-grid-cell-selected");
38056         }
38057     },
38058
38059     onCellDeselect : function(row, col){
38060         var cell = this.getCell(row, col);
38061         if(cell){
38062             Roo.fly(cell).removeClass("x-grid-cell-selected");
38063         }
38064     },
38065
38066     updateHeaderSortState : function(){
38067         
38068         // sort state can be single { field: xxx, direction : yyy}
38069         // or   { xxx=>ASC , yyy : DESC ..... }
38070         
38071         var mstate = {};
38072         if (!this.ds.multiSort) { 
38073             var state = this.ds.getSortState();
38074             if(!state){
38075                 return;
38076             }
38077             mstate[state.field] = state.direction;
38078             // FIXME... - this is not used here.. but might be elsewhere..
38079             this.sortState = state;
38080             
38081         } else {
38082             mstate = this.ds.sortToggle;
38083         }
38084         //remove existing sort classes..
38085         
38086         var sc = this.sortClasses;
38087         var hds = this.el.select(this.headerSelector).removeClass(sc);
38088         
38089         for(var f in mstate) {
38090         
38091             var sortColumn = this.cm.findColumnIndex(f);
38092             
38093             if(sortColumn != -1){
38094                 var sortDir = mstate[f];        
38095                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38096             }
38097         }
38098         
38099          
38100         
38101     },
38102
38103
38104     handleHeaderClick : function(g, index,e){
38105         
38106         Roo.log("header click");
38107         
38108         if (Roo.isTouch) {
38109             // touch events on header are handled by context
38110             this.handleHdCtx(g,index,e);
38111             return;
38112         }
38113         
38114         
38115         if(this.headersDisabled){
38116             return;
38117         }
38118         var dm = g.dataSource, cm = g.colModel;
38119         if(!cm.isSortable(index)){
38120             return;
38121         }
38122         g.stopEditing();
38123         
38124         if (dm.multiSort) {
38125             // update the sortOrder
38126             var so = [];
38127             for(var i = 0; i < cm.config.length; i++ ) {
38128                 
38129                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38130                     continue; // dont' bother, it's not in sort list or being set.
38131                 }
38132                 
38133                 so.push(cm.config[i].dataIndex);
38134             };
38135             dm.sortOrder = so;
38136         }
38137         
38138         
38139         dm.sort(cm.getDataIndex(index));
38140     },
38141
38142
38143     destroy : function(){
38144         if(this.colMenu){
38145             this.colMenu.removeAll();
38146             Roo.menu.MenuMgr.unregister(this.colMenu);
38147             this.colMenu.getEl().remove();
38148             delete this.colMenu;
38149         }
38150         if(this.hmenu){
38151             this.hmenu.removeAll();
38152             Roo.menu.MenuMgr.unregister(this.hmenu);
38153             this.hmenu.getEl().remove();
38154             delete this.hmenu;
38155         }
38156         if(this.grid.enableColumnMove){
38157             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38158             if(dds){
38159                 for(var dd in dds){
38160                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38161                         var elid = dds[dd].dragElId;
38162                         dds[dd].unreg();
38163                         Roo.get(elid).remove();
38164                     } else if(dds[dd].config.isTarget){
38165                         dds[dd].proxyTop.remove();
38166                         dds[dd].proxyBottom.remove();
38167                         dds[dd].unreg();
38168                     }
38169                     if(Roo.dd.DDM.locationCache[dd]){
38170                         delete Roo.dd.DDM.locationCache[dd];
38171                     }
38172                 }
38173                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38174             }
38175         }
38176         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38177         this.bind(null, null);
38178         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38179     },
38180
38181     handleLockChange : function(){
38182         this.refresh(true);
38183     },
38184
38185     onDenyColumnLock : function(){
38186
38187     },
38188
38189     onDenyColumnHide : function(){
38190
38191     },
38192
38193     handleHdMenuClick : function(item){
38194         var index = this.hdCtxIndex;
38195         var cm = this.cm, ds = this.ds;
38196         switch(item.id){
38197             case "asc":
38198                 ds.sort(cm.getDataIndex(index), "ASC");
38199                 break;
38200             case "desc":
38201                 ds.sort(cm.getDataIndex(index), "DESC");
38202                 break;
38203             case "lock":
38204                 var lc = cm.getLockedCount();
38205                 if(cm.getColumnCount(true) <= lc+1){
38206                     this.onDenyColumnLock();
38207                     return;
38208                 }
38209                 if(lc != index){
38210                     cm.setLocked(index, true, true);
38211                     cm.moveColumn(index, lc);
38212                     this.grid.fireEvent("columnmove", index, lc);
38213                 }else{
38214                     cm.setLocked(index, true);
38215                 }
38216             break;
38217             case "unlock":
38218                 var lc = cm.getLockedCount();
38219                 if((lc-1) != index){
38220                     cm.setLocked(index, false, true);
38221                     cm.moveColumn(index, lc-1);
38222                     this.grid.fireEvent("columnmove", index, lc-1);
38223                 }else{
38224                     cm.setLocked(index, false);
38225                 }
38226             break;
38227             case 'wider': // used to expand cols on touch..
38228             case 'narrow':
38229                 var cw = cm.getColumnWidth(index);
38230                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38231                 cw = Math.max(0, cw);
38232                 cw = Math.min(cw,4000);
38233                 cm.setColumnWidth(index, cw);
38234                 break;
38235                 
38236             default:
38237                 index = cm.getIndexById(item.id.substr(4));
38238                 if(index != -1){
38239                     if(item.checked && cm.getColumnCount(true) <= 1){
38240                         this.onDenyColumnHide();
38241                         return false;
38242                     }
38243                     cm.setHidden(index, item.checked);
38244                 }
38245         }
38246         return true;
38247     },
38248
38249     beforeColMenuShow : function(){
38250         var cm = this.cm,  colCount = cm.getColumnCount();
38251         this.colMenu.removeAll();
38252         for(var i = 0; i < colCount; i++){
38253             this.colMenu.add(new Roo.menu.CheckItem({
38254                 id: "col-"+cm.getColumnId(i),
38255                 text: cm.getColumnHeader(i),
38256                 checked: !cm.isHidden(i),
38257                 hideOnClick:false
38258             }));
38259         }
38260     },
38261
38262     handleHdCtx : function(g, index, e){
38263         e.stopEvent();
38264         var hd = this.getHeaderCell(index);
38265         this.hdCtxIndex = index;
38266         var ms = this.hmenu.items, cm = this.cm;
38267         ms.get("asc").setDisabled(!cm.isSortable(index));
38268         ms.get("desc").setDisabled(!cm.isSortable(index));
38269         if(this.grid.enableColLock !== false){
38270             ms.get("lock").setDisabled(cm.isLocked(index));
38271             ms.get("unlock").setDisabled(!cm.isLocked(index));
38272         }
38273         this.hmenu.show(hd, "tl-bl");
38274     },
38275
38276     handleHdOver : function(e){
38277         var hd = this.findHeaderCell(e.getTarget());
38278         if(hd && !this.headersDisabled){
38279             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38280                this.fly(hd).addClass("x-grid-hd-over");
38281             }
38282         }
38283     },
38284
38285     handleHdOut : function(e){
38286         var hd = this.findHeaderCell(e.getTarget());
38287         if(hd){
38288             this.fly(hd).removeClass("x-grid-hd-over");
38289         }
38290     },
38291
38292     handleSplitDblClick : function(e, t){
38293         var i = this.getCellIndex(t);
38294         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38295             this.autoSizeColumn(i, true);
38296             this.layout();
38297         }
38298     },
38299
38300     render : function(){
38301
38302         var cm = this.cm;
38303         var colCount = cm.getColumnCount();
38304
38305         if(this.grid.monitorWindowResize === true){
38306             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38307         }
38308         var header = this.renderHeaders();
38309         var body = this.templates.body.apply({rows:""});
38310         var html = this.templates.master.apply({
38311             lockedBody: body,
38312             body: body,
38313             lockedHeader: header[0],
38314             header: header[1]
38315         });
38316
38317         //this.updateColumns();
38318
38319         this.grid.getGridEl().dom.innerHTML = html;
38320
38321         this.initElements();
38322         
38323         // a kludge to fix the random scolling effect in webkit
38324         this.el.on("scroll", function() {
38325             this.el.dom.scrollTop=0; // hopefully not recursive..
38326         },this);
38327
38328         this.scroller.on("scroll", this.handleScroll, this);
38329         this.lockedBody.on("mousewheel", this.handleWheel, this);
38330         this.mainBody.on("mousewheel", this.handleWheel, this);
38331
38332         this.mainHd.on("mouseover", this.handleHdOver, this);
38333         this.mainHd.on("mouseout", this.handleHdOut, this);
38334         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38335                 {delegate: "."+this.splitClass});
38336
38337         this.lockedHd.on("mouseover", this.handleHdOver, this);
38338         this.lockedHd.on("mouseout", this.handleHdOut, this);
38339         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38340                 {delegate: "."+this.splitClass});
38341
38342         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38343             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38344         }
38345
38346         this.updateSplitters();
38347
38348         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38349             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38350             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38351         }
38352
38353         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38354             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38355             this.hmenu.add(
38356                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38357                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38358             );
38359             if(this.grid.enableColLock !== false){
38360                 this.hmenu.add('-',
38361                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38362                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38363                 );
38364             }
38365             if (Roo.isTouch) {
38366                  this.hmenu.add('-',
38367                     {id:"wider", text: this.columnsWiderText},
38368                     {id:"narrow", text: this.columnsNarrowText }
38369                 );
38370                 
38371                  
38372             }
38373             
38374             if(this.grid.enableColumnHide !== false){
38375
38376                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38377                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38378                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38379
38380                 this.hmenu.add('-',
38381                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38382                 );
38383             }
38384             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38385
38386             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38387         }
38388
38389         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38390             this.dd = new Roo.grid.GridDragZone(this.grid, {
38391                 ddGroup : this.grid.ddGroup || 'GridDD'
38392             });
38393             
38394         }
38395
38396         /*
38397         for(var i = 0; i < colCount; i++){
38398             if(cm.isHidden(i)){
38399                 this.hideColumn(i);
38400             }
38401             if(cm.config[i].align){
38402                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38403                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38404             }
38405         }*/
38406         
38407         this.updateHeaderSortState();
38408
38409         this.beforeInitialResize();
38410         this.layout(true);
38411
38412         // two part rendering gives faster view to the user
38413         this.renderPhase2.defer(1, this);
38414     },
38415
38416     renderPhase2 : function(){
38417         // render the rows now
38418         this.refresh();
38419         if(this.grid.autoSizeColumns){
38420             this.autoSizeColumns();
38421         }
38422     },
38423
38424     beforeInitialResize : function(){
38425
38426     },
38427
38428     onColumnSplitterMoved : function(i, w){
38429         this.userResized = true;
38430         var cm = this.grid.colModel;
38431         cm.setColumnWidth(i, w, true);
38432         var cid = cm.getColumnId(i);
38433         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38434         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38435         this.updateSplitters();
38436         this.layout();
38437         this.grid.fireEvent("columnresize", i, w);
38438     },
38439
38440     syncRowHeights : function(startIndex, endIndex){
38441         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38442             startIndex = startIndex || 0;
38443             var mrows = this.getBodyTable().rows;
38444             var lrows = this.getLockedTable().rows;
38445             var len = mrows.length-1;
38446             endIndex = Math.min(endIndex || len, len);
38447             for(var i = startIndex; i <= endIndex; i++){
38448                 var m = mrows[i], l = lrows[i];
38449                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38450                 m.style.height = l.style.height = h + "px";
38451             }
38452         }
38453     },
38454
38455     layout : function(initialRender, is2ndPass){
38456         var g = this.grid;
38457         var auto = g.autoHeight;
38458         var scrollOffset = 16;
38459         var c = g.getGridEl(), cm = this.cm,
38460                 expandCol = g.autoExpandColumn,
38461                 gv = this;
38462         //c.beginMeasure();
38463
38464         if(!c.dom.offsetWidth){ // display:none?
38465             if(initialRender){
38466                 this.lockedWrap.show();
38467                 this.mainWrap.show();
38468             }
38469             return;
38470         }
38471
38472         var hasLock = this.cm.isLocked(0);
38473
38474         var tbh = this.headerPanel.getHeight();
38475         var bbh = this.footerPanel.getHeight();
38476
38477         if(auto){
38478             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38479             var newHeight = ch + c.getBorderWidth("tb");
38480             if(g.maxHeight){
38481                 newHeight = Math.min(g.maxHeight, newHeight);
38482             }
38483             c.setHeight(newHeight);
38484         }
38485
38486         if(g.autoWidth){
38487             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38488         }
38489
38490         var s = this.scroller;
38491
38492         var csize = c.getSize(true);
38493
38494         this.el.setSize(csize.width, csize.height);
38495
38496         this.headerPanel.setWidth(csize.width);
38497         this.footerPanel.setWidth(csize.width);
38498
38499         var hdHeight = this.mainHd.getHeight();
38500         var vw = csize.width;
38501         var vh = csize.height - (tbh + bbh);
38502
38503         s.setSize(vw, vh);
38504
38505         var bt = this.getBodyTable();
38506         var ltWidth = hasLock ?
38507                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38508
38509         var scrollHeight = bt.offsetHeight;
38510         var scrollWidth = ltWidth + bt.offsetWidth;
38511         var vscroll = false, hscroll = false;
38512
38513         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38514
38515         var lw = this.lockedWrap, mw = this.mainWrap;
38516         var lb = this.lockedBody, mb = this.mainBody;
38517
38518         setTimeout(function(){
38519             var t = s.dom.offsetTop;
38520             var w = s.dom.clientWidth,
38521                 h = s.dom.clientHeight;
38522
38523             lw.setTop(t);
38524             lw.setSize(ltWidth, h);
38525
38526             mw.setLeftTop(ltWidth, t);
38527             mw.setSize(w-ltWidth, h);
38528
38529             lb.setHeight(h-hdHeight);
38530             mb.setHeight(h-hdHeight);
38531
38532             if(is2ndPass !== true && !gv.userResized && expandCol){
38533                 // high speed resize without full column calculation
38534                 
38535                 var ci = cm.getIndexById(expandCol);
38536                 if (ci < 0) {
38537                     ci = cm.findColumnIndex(expandCol);
38538                 }
38539                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38540                 var expandId = cm.getColumnId(ci);
38541                 var  tw = cm.getTotalWidth(false);
38542                 var currentWidth = cm.getColumnWidth(ci);
38543                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38544                 if(currentWidth != cw){
38545                     cm.setColumnWidth(ci, cw, true);
38546                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38547                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38548                     gv.updateSplitters();
38549                     gv.layout(false, true);
38550                 }
38551             }
38552
38553             if(initialRender){
38554                 lw.show();
38555                 mw.show();
38556             }
38557             //c.endMeasure();
38558         }, 10);
38559     },
38560
38561     onWindowResize : function(){
38562         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38563             return;
38564         }
38565         this.layout();
38566     },
38567
38568     appendFooter : function(parentEl){
38569         return null;
38570     },
38571
38572     sortAscText : "Sort Ascending",
38573     sortDescText : "Sort Descending",
38574     lockText : "Lock Column",
38575     unlockText : "Unlock Column",
38576     columnsText : "Columns",
38577  
38578     columnsWiderText : "Wider",
38579     columnsNarrowText : "Thinner"
38580 });
38581
38582
38583 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38584     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38585     this.proxy.el.addClass('x-grid3-col-dd');
38586 };
38587
38588 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38589     handleMouseDown : function(e){
38590
38591     },
38592
38593     callHandleMouseDown : function(e){
38594         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38595     }
38596 });
38597 /*
38598  * Based on:
38599  * Ext JS Library 1.1.1
38600  * Copyright(c) 2006-2007, Ext JS, LLC.
38601  *
38602  * Originally Released Under LGPL - original licence link has changed is not relivant.
38603  *
38604  * Fork - LGPL
38605  * <script type="text/javascript">
38606  */
38607  
38608 // private
38609 // This is a support class used internally by the Grid components
38610 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38611     this.grid = grid;
38612     this.view = grid.getView();
38613     this.proxy = this.view.resizeProxy;
38614     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38615         "gridSplitters" + this.grid.getGridEl().id, {
38616         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38617     });
38618     this.setHandleElId(Roo.id(hd));
38619     this.setOuterHandleElId(Roo.id(hd2));
38620     this.scroll = false;
38621 };
38622 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38623     fly: Roo.Element.fly,
38624
38625     b4StartDrag : function(x, y){
38626         this.view.headersDisabled = true;
38627         this.proxy.setHeight(this.view.mainWrap.getHeight());
38628         var w = this.cm.getColumnWidth(this.cellIndex);
38629         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38630         this.resetConstraints();
38631         this.setXConstraint(minw, 1000);
38632         this.setYConstraint(0, 0);
38633         this.minX = x - minw;
38634         this.maxX = x + 1000;
38635         this.startPos = x;
38636         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38637     },
38638
38639
38640     handleMouseDown : function(e){
38641         ev = Roo.EventObject.setEvent(e);
38642         var t = this.fly(ev.getTarget());
38643         if(t.hasClass("x-grid-split")){
38644             this.cellIndex = this.view.getCellIndex(t.dom);
38645             this.split = t.dom;
38646             this.cm = this.grid.colModel;
38647             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38648                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38649             }
38650         }
38651     },
38652
38653     endDrag : function(e){
38654         this.view.headersDisabled = false;
38655         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38656         var diff = endX - this.startPos;
38657         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38658     },
38659
38660     autoOffset : function(){
38661         this.setDelta(0,0);
38662     }
38663 });/*
38664  * Based on:
38665  * Ext JS Library 1.1.1
38666  * Copyright(c) 2006-2007, Ext JS, LLC.
38667  *
38668  * Originally Released Under LGPL - original licence link has changed is not relivant.
38669  *
38670  * Fork - LGPL
38671  * <script type="text/javascript">
38672  */
38673  
38674 // private
38675 // This is a support class used internally by the Grid components
38676 Roo.grid.GridDragZone = function(grid, config){
38677     this.view = grid.getView();
38678     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38679     if(this.view.lockedBody){
38680         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38681         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38682     }
38683     this.scroll = false;
38684     this.grid = grid;
38685     this.ddel = document.createElement('div');
38686     this.ddel.className = 'x-grid-dd-wrap';
38687 };
38688
38689 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38690     ddGroup : "GridDD",
38691
38692     getDragData : function(e){
38693         var t = Roo.lib.Event.getTarget(e);
38694         var rowIndex = this.view.findRowIndex(t);
38695         var sm = this.grid.selModel;
38696             
38697         //Roo.log(rowIndex);
38698         
38699         if (sm.getSelectedCell) {
38700             // cell selection..
38701             if (!sm.getSelectedCell()) {
38702                 return false;
38703             }
38704             if (rowIndex != sm.getSelectedCell()[0]) {
38705                 return false;
38706             }
38707         
38708         }
38709         
38710         if(rowIndex !== false){
38711             
38712             // if editorgrid.. 
38713             
38714             
38715             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38716                
38717             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38718               //  
38719             //}
38720             if (e.hasModifier()){
38721                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38722             }
38723             
38724             Roo.log("getDragData");
38725             
38726             return {
38727                 grid: this.grid,
38728                 ddel: this.ddel,
38729                 rowIndex: rowIndex,
38730                 selections:sm.getSelections ? sm.getSelections() : (
38731                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38732                 )
38733             };
38734         }
38735         return false;
38736     },
38737
38738     onInitDrag : function(e){
38739         var data = this.dragData;
38740         this.ddel.innerHTML = this.grid.getDragDropText();
38741         this.proxy.update(this.ddel);
38742         // fire start drag?
38743     },
38744
38745     afterRepair : function(){
38746         this.dragging = false;
38747     },
38748
38749     getRepairXY : function(e, data){
38750         return false;
38751     },
38752
38753     onEndDrag : function(data, e){
38754         // fire end drag?
38755     },
38756
38757     onValidDrop : function(dd, e, id){
38758         // fire drag drop?
38759         this.hideProxy();
38760     },
38761
38762     beforeInvalidDrop : function(e, id){
38763
38764     }
38765 });/*
38766  * Based on:
38767  * Ext JS Library 1.1.1
38768  * Copyright(c) 2006-2007, Ext JS, LLC.
38769  *
38770  * Originally Released Under LGPL - original licence link has changed is not relivant.
38771  *
38772  * Fork - LGPL
38773  * <script type="text/javascript">
38774  */
38775  
38776
38777 /**
38778  * @class Roo.grid.ColumnModel
38779  * @extends Roo.util.Observable
38780  * This is the default implementation of a ColumnModel used by the Grid. It defines
38781  * the columns in the grid.
38782  * <br>Usage:<br>
38783  <pre><code>
38784  var colModel = new Roo.grid.ColumnModel([
38785         {header: "Ticker", width: 60, sortable: true, locked: true},
38786         {header: "Company Name", width: 150, sortable: true},
38787         {header: "Market Cap.", width: 100, sortable: true},
38788         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38789         {header: "Employees", width: 100, sortable: true, resizable: false}
38790  ]);
38791  </code></pre>
38792  * <p>
38793  
38794  * The config options listed for this class are options which may appear in each
38795  * individual column definition.
38796  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38797  * @constructor
38798  * @param {Object} config An Array of column config objects. See this class's
38799  * config objects for details.
38800 */
38801 Roo.grid.ColumnModel = function(config){
38802         /**
38803      * The config passed into the constructor
38804      */
38805     this.config = config;
38806     this.lookup = {};
38807
38808     // if no id, create one
38809     // if the column does not have a dataIndex mapping,
38810     // map it to the order it is in the config
38811     for(var i = 0, len = config.length; i < len; i++){
38812         var c = config[i];
38813         if(typeof c.dataIndex == "undefined"){
38814             c.dataIndex = i;
38815         }
38816         if(typeof c.renderer == "string"){
38817             c.renderer = Roo.util.Format[c.renderer];
38818         }
38819         if(typeof c.id == "undefined"){
38820             c.id = Roo.id();
38821         }
38822         if(c.editor && c.editor.xtype){
38823             c.editor  = Roo.factory(c.editor, Roo.grid);
38824         }
38825         if(c.editor && c.editor.isFormField){
38826             c.editor = new Roo.grid.GridEditor(c.editor);
38827         }
38828         this.lookup[c.id] = c;
38829     }
38830
38831     /**
38832      * The width of columns which have no width specified (defaults to 100)
38833      * @type Number
38834      */
38835     this.defaultWidth = 100;
38836
38837     /**
38838      * Default sortable of columns which have no sortable specified (defaults to false)
38839      * @type Boolean
38840      */
38841     this.defaultSortable = false;
38842
38843     this.addEvents({
38844         /**
38845              * @event widthchange
38846              * Fires when the width of a column changes.
38847              * @param {ColumnModel} this
38848              * @param {Number} columnIndex The column index
38849              * @param {Number} newWidth The new width
38850              */
38851             "widthchange": true,
38852         /**
38853              * @event headerchange
38854              * Fires when the text of a header changes.
38855              * @param {ColumnModel} this
38856              * @param {Number} columnIndex The column index
38857              * @param {Number} newText The new header text
38858              */
38859             "headerchange": true,
38860         /**
38861              * @event hiddenchange
38862              * Fires when a column is hidden or "unhidden".
38863              * @param {ColumnModel} this
38864              * @param {Number} columnIndex The column index
38865              * @param {Boolean} hidden true if hidden, false otherwise
38866              */
38867             "hiddenchange": true,
38868             /**
38869          * @event columnmoved
38870          * Fires when a column is moved.
38871          * @param {ColumnModel} this
38872          * @param {Number} oldIndex
38873          * @param {Number} newIndex
38874          */
38875         "columnmoved" : true,
38876         /**
38877          * @event columlockchange
38878          * Fires when a column's locked state is changed
38879          * @param {ColumnModel} this
38880          * @param {Number} colIndex
38881          * @param {Boolean} locked true if locked
38882          */
38883         "columnlockchange" : true
38884     });
38885     Roo.grid.ColumnModel.superclass.constructor.call(this);
38886 };
38887 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38888     /**
38889      * @cfg {String} header The header text to display in the Grid view.
38890      */
38891     /**
38892      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38893      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38894      * specified, the column's index is used as an index into the Record's data Array.
38895      */
38896     /**
38897      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38898      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38899      */
38900     /**
38901      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38902      * Defaults to the value of the {@link #defaultSortable} property.
38903      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38904      */
38905     /**
38906      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38907      */
38908     /**
38909      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38910      */
38911     /**
38912      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38913      */
38914     /**
38915      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38916      */
38917     /**
38918      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38919      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38920      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38921      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38922      */
38923        /**
38924      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38925      */
38926     /**
38927      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38928      */
38929     /**
38930      * @cfg {String} cursor (Optional)
38931      */
38932     /**
38933      * @cfg {String} tooltip (Optional)
38934      */
38935     /**
38936      * Returns the id of the column at the specified index.
38937      * @param {Number} index The column index
38938      * @return {String} the id
38939      */
38940     getColumnId : function(index){
38941         return this.config[index].id;
38942     },
38943
38944     /**
38945      * Returns the column for a specified id.
38946      * @param {String} id The column id
38947      * @return {Object} the column
38948      */
38949     getColumnById : function(id){
38950         return this.lookup[id];
38951     },
38952
38953     
38954     /**
38955      * Returns the column for a specified dataIndex.
38956      * @param {String} dataIndex The column dataIndex
38957      * @return {Object|Boolean} the column or false if not found
38958      */
38959     getColumnByDataIndex: function(dataIndex){
38960         var index = this.findColumnIndex(dataIndex);
38961         return index > -1 ? this.config[index] : false;
38962     },
38963     
38964     /**
38965      * Returns the index for a specified column id.
38966      * @param {String} id The column id
38967      * @return {Number} the index, or -1 if not found
38968      */
38969     getIndexById : function(id){
38970         for(var i = 0, len = this.config.length; i < len; i++){
38971             if(this.config[i].id == id){
38972                 return i;
38973             }
38974         }
38975         return -1;
38976     },
38977     
38978     /**
38979      * Returns the index for a specified column dataIndex.
38980      * @param {String} dataIndex The column dataIndex
38981      * @return {Number} the index, or -1 if not found
38982      */
38983     
38984     findColumnIndex : function(dataIndex){
38985         for(var i = 0, len = this.config.length; i < len; i++){
38986             if(this.config[i].dataIndex == dataIndex){
38987                 return i;
38988             }
38989         }
38990         return -1;
38991     },
38992     
38993     
38994     moveColumn : function(oldIndex, newIndex){
38995         var c = this.config[oldIndex];
38996         this.config.splice(oldIndex, 1);
38997         this.config.splice(newIndex, 0, c);
38998         this.dataMap = null;
38999         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39000     },
39001
39002     isLocked : function(colIndex){
39003         return this.config[colIndex].locked === true;
39004     },
39005
39006     setLocked : function(colIndex, value, suppressEvent){
39007         if(this.isLocked(colIndex) == value){
39008             return;
39009         }
39010         this.config[colIndex].locked = value;
39011         if(!suppressEvent){
39012             this.fireEvent("columnlockchange", this, colIndex, value);
39013         }
39014     },
39015
39016     getTotalLockedWidth : function(){
39017         var totalWidth = 0;
39018         for(var i = 0; i < this.config.length; i++){
39019             if(this.isLocked(i) && !this.isHidden(i)){
39020                 this.totalWidth += this.getColumnWidth(i);
39021             }
39022         }
39023         return totalWidth;
39024     },
39025
39026     getLockedCount : function(){
39027         for(var i = 0, len = this.config.length; i < len; i++){
39028             if(!this.isLocked(i)){
39029                 return i;
39030             }
39031         }
39032     },
39033
39034     /**
39035      * Returns the number of columns.
39036      * @return {Number}
39037      */
39038     getColumnCount : function(visibleOnly){
39039         if(visibleOnly === true){
39040             var c = 0;
39041             for(var i = 0, len = this.config.length; i < len; i++){
39042                 if(!this.isHidden(i)){
39043                     c++;
39044                 }
39045             }
39046             return c;
39047         }
39048         return this.config.length;
39049     },
39050
39051     /**
39052      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39053      * @param {Function} fn
39054      * @param {Object} scope (optional)
39055      * @return {Array} result
39056      */
39057     getColumnsBy : function(fn, scope){
39058         var r = [];
39059         for(var i = 0, len = this.config.length; i < len; i++){
39060             var c = this.config[i];
39061             if(fn.call(scope||this, c, i) === true){
39062                 r[r.length] = c;
39063             }
39064         }
39065         return r;
39066     },
39067
39068     /**
39069      * Returns true if the specified column is sortable.
39070      * @param {Number} col The column index
39071      * @return {Boolean}
39072      */
39073     isSortable : function(col){
39074         if(typeof this.config[col].sortable == "undefined"){
39075             return this.defaultSortable;
39076         }
39077         return this.config[col].sortable;
39078     },
39079
39080     /**
39081      * Returns the rendering (formatting) function defined for the column.
39082      * @param {Number} col The column index.
39083      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39084      */
39085     getRenderer : function(col){
39086         if(!this.config[col].renderer){
39087             return Roo.grid.ColumnModel.defaultRenderer;
39088         }
39089         return this.config[col].renderer;
39090     },
39091
39092     /**
39093      * Sets the rendering (formatting) function for a column.
39094      * @param {Number} col The column index
39095      * @param {Function} fn The function to use to process the cell's raw data
39096      * to return HTML markup for the grid view. The render function is called with
39097      * the following parameters:<ul>
39098      * <li>Data value.</li>
39099      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39100      * <li>css A CSS style string to apply to the table cell.</li>
39101      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39102      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39103      * <li>Row index</li>
39104      * <li>Column index</li>
39105      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39106      */
39107     setRenderer : function(col, fn){
39108         this.config[col].renderer = fn;
39109     },
39110
39111     /**
39112      * Returns the width for the specified column.
39113      * @param {Number} col The column index
39114      * @return {Number}
39115      */
39116     getColumnWidth : function(col){
39117         return this.config[col].width * 1 || this.defaultWidth;
39118     },
39119
39120     /**
39121      * Sets the width for a column.
39122      * @param {Number} col The column index
39123      * @param {Number} width The new width
39124      */
39125     setColumnWidth : function(col, width, suppressEvent){
39126         this.config[col].width = width;
39127         this.totalWidth = null;
39128         if(!suppressEvent){
39129              this.fireEvent("widthchange", this, col, width);
39130         }
39131     },
39132
39133     /**
39134      * Returns the total width of all columns.
39135      * @param {Boolean} includeHidden True to include hidden column widths
39136      * @return {Number}
39137      */
39138     getTotalWidth : function(includeHidden){
39139         if(!this.totalWidth){
39140             this.totalWidth = 0;
39141             for(var i = 0, len = this.config.length; i < len; i++){
39142                 if(includeHidden || !this.isHidden(i)){
39143                     this.totalWidth += this.getColumnWidth(i);
39144                 }
39145             }
39146         }
39147         return this.totalWidth;
39148     },
39149
39150     /**
39151      * Returns the header for the specified column.
39152      * @param {Number} col The column index
39153      * @return {String}
39154      */
39155     getColumnHeader : function(col){
39156         return this.config[col].header;
39157     },
39158
39159     /**
39160      * Sets the header for a column.
39161      * @param {Number} col The column index
39162      * @param {String} header The new header
39163      */
39164     setColumnHeader : function(col, header){
39165         this.config[col].header = header;
39166         this.fireEvent("headerchange", this, col, header);
39167     },
39168
39169     /**
39170      * Returns the tooltip for the specified column.
39171      * @param {Number} col The column index
39172      * @return {String}
39173      */
39174     getColumnTooltip : function(col){
39175             return this.config[col].tooltip;
39176     },
39177     /**
39178      * Sets the tooltip for a column.
39179      * @param {Number} col The column index
39180      * @param {String} tooltip The new tooltip
39181      */
39182     setColumnTooltip : function(col, tooltip){
39183             this.config[col].tooltip = tooltip;
39184     },
39185
39186     /**
39187      * Returns the dataIndex for the specified column.
39188      * @param {Number} col The column index
39189      * @return {Number}
39190      */
39191     getDataIndex : function(col){
39192         return this.config[col].dataIndex;
39193     },
39194
39195     /**
39196      * Sets the dataIndex for a column.
39197      * @param {Number} col The column index
39198      * @param {Number} dataIndex The new dataIndex
39199      */
39200     setDataIndex : function(col, dataIndex){
39201         this.config[col].dataIndex = dataIndex;
39202     },
39203
39204     
39205     
39206     /**
39207      * Returns true if the cell is editable.
39208      * @param {Number} colIndex The column index
39209      * @param {Number} rowIndex The row index
39210      * @return {Boolean}
39211      */
39212     isCellEditable : function(colIndex, rowIndex){
39213         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39214     },
39215
39216     /**
39217      * Returns the editor defined for the cell/column.
39218      * return false or null to disable editing.
39219      * @param {Number} colIndex The column index
39220      * @param {Number} rowIndex The row index
39221      * @return {Object}
39222      */
39223     getCellEditor : function(colIndex, rowIndex){
39224         return this.config[colIndex].editor;
39225     },
39226
39227     /**
39228      * Sets if a column is editable.
39229      * @param {Number} col The column index
39230      * @param {Boolean} editable True if the column is editable
39231      */
39232     setEditable : function(col, editable){
39233         this.config[col].editable = editable;
39234     },
39235
39236
39237     /**
39238      * Returns true if the column is hidden.
39239      * @param {Number} colIndex The column index
39240      * @return {Boolean}
39241      */
39242     isHidden : function(colIndex){
39243         return this.config[colIndex].hidden;
39244     },
39245
39246
39247     /**
39248      * Returns true if the column width cannot be changed
39249      */
39250     isFixed : function(colIndex){
39251         return this.config[colIndex].fixed;
39252     },
39253
39254     /**
39255      * Returns true if the column can be resized
39256      * @return {Boolean}
39257      */
39258     isResizable : function(colIndex){
39259         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39260     },
39261     /**
39262      * Sets if a column is hidden.
39263      * @param {Number} colIndex The column index
39264      * @param {Boolean} hidden True if the column is hidden
39265      */
39266     setHidden : function(colIndex, hidden){
39267         this.config[colIndex].hidden = hidden;
39268         this.totalWidth = null;
39269         this.fireEvent("hiddenchange", this, colIndex, hidden);
39270     },
39271
39272     /**
39273      * Sets the editor for a column.
39274      * @param {Number} col The column index
39275      * @param {Object} editor The editor object
39276      */
39277     setEditor : function(col, editor){
39278         this.config[col].editor = editor;
39279     }
39280 });
39281
39282 Roo.grid.ColumnModel.defaultRenderer = function(value){
39283         if(typeof value == "string" && value.length < 1){
39284             return "&#160;";
39285         }
39286         return value;
39287 };
39288
39289 // Alias for backwards compatibility
39290 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39291 /*
39292  * Based on:
39293  * Ext JS Library 1.1.1
39294  * Copyright(c) 2006-2007, Ext JS, LLC.
39295  *
39296  * Originally Released Under LGPL - original licence link has changed is not relivant.
39297  *
39298  * Fork - LGPL
39299  * <script type="text/javascript">
39300  */
39301
39302 /**
39303  * @class Roo.grid.AbstractSelectionModel
39304  * @extends Roo.util.Observable
39305  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39306  * implemented by descendant classes.  This class should not be directly instantiated.
39307  * @constructor
39308  */
39309 Roo.grid.AbstractSelectionModel = function(){
39310     this.locked = false;
39311     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39312 };
39313
39314 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39315     /** @ignore Called by the grid automatically. Do not call directly. */
39316     init : function(grid){
39317         this.grid = grid;
39318         this.initEvents();
39319     },
39320
39321     /**
39322      * Locks the selections.
39323      */
39324     lock : function(){
39325         this.locked = true;
39326     },
39327
39328     /**
39329      * Unlocks the selections.
39330      */
39331     unlock : function(){
39332         this.locked = false;
39333     },
39334
39335     /**
39336      * Returns true if the selections are locked.
39337      * @return {Boolean}
39338      */
39339     isLocked : function(){
39340         return this.locked;
39341     }
39342 });/*
39343  * Based on:
39344  * Ext JS Library 1.1.1
39345  * Copyright(c) 2006-2007, Ext JS, LLC.
39346  *
39347  * Originally Released Under LGPL - original licence link has changed is not relivant.
39348  *
39349  * Fork - LGPL
39350  * <script type="text/javascript">
39351  */
39352 /**
39353  * @extends Roo.grid.AbstractSelectionModel
39354  * @class Roo.grid.RowSelectionModel
39355  * The default SelectionModel used by {@link Roo.grid.Grid}.
39356  * It supports multiple selections and keyboard selection/navigation. 
39357  * @constructor
39358  * @param {Object} config
39359  */
39360 Roo.grid.RowSelectionModel = function(config){
39361     Roo.apply(this, config);
39362     this.selections = new Roo.util.MixedCollection(false, function(o){
39363         return o.id;
39364     });
39365
39366     this.last = false;
39367     this.lastActive = false;
39368
39369     this.addEvents({
39370         /**
39371              * @event selectionchange
39372              * Fires when the selection changes
39373              * @param {SelectionModel} this
39374              */
39375             "selectionchange" : true,
39376         /**
39377              * @event afterselectionchange
39378              * Fires after the selection changes (eg. by key press or clicking)
39379              * @param {SelectionModel} this
39380              */
39381             "afterselectionchange" : true,
39382         /**
39383              * @event beforerowselect
39384              * Fires when a row is selected being selected, return false to cancel.
39385              * @param {SelectionModel} this
39386              * @param {Number} rowIndex The selected index
39387              * @param {Boolean} keepExisting False if other selections will be cleared
39388              */
39389             "beforerowselect" : true,
39390         /**
39391              * @event rowselect
39392              * Fires when a row is selected.
39393              * @param {SelectionModel} this
39394              * @param {Number} rowIndex The selected index
39395              * @param {Roo.data.Record} r The record
39396              */
39397             "rowselect" : true,
39398         /**
39399              * @event rowdeselect
39400              * Fires when a row is deselected.
39401              * @param {SelectionModel} this
39402              * @param {Number} rowIndex The selected index
39403              */
39404         "rowdeselect" : true
39405     });
39406     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39407     this.locked = false;
39408 };
39409
39410 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39411     /**
39412      * @cfg {Boolean} singleSelect
39413      * True to allow selection of only one row at a time (defaults to false)
39414      */
39415     singleSelect : false,
39416
39417     // private
39418     initEvents : function(){
39419
39420         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39421             this.grid.on("mousedown", this.handleMouseDown, this);
39422         }else{ // allow click to work like normal
39423             this.grid.on("rowclick", this.handleDragableRowClick, this);
39424         }
39425
39426         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39427             "up" : function(e){
39428                 if(!e.shiftKey){
39429                     this.selectPrevious(e.shiftKey);
39430                 }else if(this.last !== false && this.lastActive !== false){
39431                     var last = this.last;
39432                     this.selectRange(this.last,  this.lastActive-1);
39433                     this.grid.getView().focusRow(this.lastActive);
39434                     if(last !== false){
39435                         this.last = last;
39436                     }
39437                 }else{
39438                     this.selectFirstRow();
39439                 }
39440                 this.fireEvent("afterselectionchange", this);
39441             },
39442             "down" : function(e){
39443                 if(!e.shiftKey){
39444                     this.selectNext(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             scope: this
39458         });
39459
39460         var view = this.grid.view;
39461         view.on("refresh", this.onRefresh, this);
39462         view.on("rowupdated", this.onRowUpdated, this);
39463         view.on("rowremoved", this.onRemove, this);
39464     },
39465
39466     // private
39467     onRefresh : function(){
39468         var ds = this.grid.dataSource, i, v = this.grid.view;
39469         var s = this.selections;
39470         s.each(function(r){
39471             if((i = ds.indexOfId(r.id)) != -1){
39472                 v.onRowSelect(i);
39473                 s.add(ds.getAt(i)); // updating the selection relate data
39474             }else{
39475                 s.remove(r);
39476             }
39477         });
39478     },
39479
39480     // private
39481     onRemove : function(v, index, r){
39482         this.selections.remove(r);
39483     },
39484
39485     // private
39486     onRowUpdated : function(v, index, r){
39487         if(this.isSelected(r)){
39488             v.onRowSelect(index);
39489         }
39490     },
39491
39492     /**
39493      * Select records.
39494      * @param {Array} records The records to select
39495      * @param {Boolean} keepExisting (optional) True to keep existing selections
39496      */
39497     selectRecords : function(records, keepExisting){
39498         if(!keepExisting){
39499             this.clearSelections();
39500         }
39501         var ds = this.grid.dataSource;
39502         for(var i = 0, len = records.length; i < len; i++){
39503             this.selectRow(ds.indexOf(records[i]), true);
39504         }
39505     },
39506
39507     /**
39508      * Gets the number of selected rows.
39509      * @return {Number}
39510      */
39511     getCount : function(){
39512         return this.selections.length;
39513     },
39514
39515     /**
39516      * Selects the first row in the grid.
39517      */
39518     selectFirstRow : function(){
39519         this.selectRow(0);
39520     },
39521
39522     /**
39523      * Select the last row.
39524      * @param {Boolean} keepExisting (optional) True to keep existing selections
39525      */
39526     selectLastRow : function(keepExisting){
39527         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39528     },
39529
39530     /**
39531      * Selects the row immediately following the last selected row.
39532      * @param {Boolean} keepExisting (optional) True to keep existing selections
39533      */
39534     selectNext : function(keepExisting){
39535         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39536             this.selectRow(this.last+1, keepExisting);
39537             this.grid.getView().focusRow(this.last);
39538         }
39539     },
39540
39541     /**
39542      * Selects the row that precedes the last selected row.
39543      * @param {Boolean} keepExisting (optional) True to keep existing selections
39544      */
39545     selectPrevious : function(keepExisting){
39546         if(this.last){
39547             this.selectRow(this.last-1, keepExisting);
39548             this.grid.getView().focusRow(this.last);
39549         }
39550     },
39551
39552     /**
39553      * Returns the selected records
39554      * @return {Array} Array of selected records
39555      */
39556     getSelections : function(){
39557         return [].concat(this.selections.items);
39558     },
39559
39560     /**
39561      * Returns the first selected record.
39562      * @return {Record}
39563      */
39564     getSelected : function(){
39565         return this.selections.itemAt(0);
39566     },
39567
39568
39569     /**
39570      * Clears all selections.
39571      */
39572     clearSelections : function(fast){
39573         if(this.locked) return;
39574         if(fast !== true){
39575             var ds = this.grid.dataSource;
39576             var s = this.selections;
39577             s.each(function(r){
39578                 this.deselectRow(ds.indexOfId(r.id));
39579             }, this);
39580             s.clear();
39581         }else{
39582             this.selections.clear();
39583         }
39584         this.last = false;
39585     },
39586
39587
39588     /**
39589      * Selects all rows.
39590      */
39591     selectAll : function(){
39592         if(this.locked) return;
39593         this.selections.clear();
39594         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39595             this.selectRow(i, true);
39596         }
39597     },
39598
39599     /**
39600      * Returns True if there is a selection.
39601      * @return {Boolean}
39602      */
39603     hasSelection : function(){
39604         return this.selections.length > 0;
39605     },
39606
39607     /**
39608      * Returns True if the specified row is selected.
39609      * @param {Number/Record} record The record or index of the record to check
39610      * @return {Boolean}
39611      */
39612     isSelected : function(index){
39613         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39614         return (r && this.selections.key(r.id) ? true : false);
39615     },
39616
39617     /**
39618      * Returns True if the specified record id is selected.
39619      * @param {String} id The id of record to check
39620      * @return {Boolean}
39621      */
39622     isIdSelected : function(id){
39623         return (this.selections.key(id) ? true : false);
39624     },
39625
39626     // private
39627     handleMouseDown : function(e, t){
39628         var view = this.grid.getView(), rowIndex;
39629         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39630             return;
39631         };
39632         if(e.shiftKey && this.last !== false){
39633             var last = this.last;
39634             this.selectRange(last, rowIndex, e.ctrlKey);
39635             this.last = last; // reset the last
39636             view.focusRow(rowIndex);
39637         }else{
39638             var isSelected = this.isSelected(rowIndex);
39639             if(e.button !== 0 && isSelected){
39640                 view.focusRow(rowIndex);
39641             }else if(e.ctrlKey && isSelected){
39642                 this.deselectRow(rowIndex);
39643             }else if(!isSelected){
39644                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39645                 view.focusRow(rowIndex);
39646             }
39647         }
39648         this.fireEvent("afterselectionchange", this);
39649     },
39650     // private
39651     handleDragableRowClick :  function(grid, rowIndex, e) 
39652     {
39653         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39654             this.selectRow(rowIndex, false);
39655             grid.view.focusRow(rowIndex);
39656              this.fireEvent("afterselectionchange", this);
39657         }
39658     },
39659     
39660     /**
39661      * Selects multiple rows.
39662      * @param {Array} rows Array of the indexes of the row to select
39663      * @param {Boolean} keepExisting (optional) True to keep existing selections
39664      */
39665     selectRows : function(rows, keepExisting){
39666         if(!keepExisting){
39667             this.clearSelections();
39668         }
39669         for(var i = 0, len = rows.length; i < len; i++){
39670             this.selectRow(rows[i], true);
39671         }
39672     },
39673
39674     /**
39675      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39676      * @param {Number} startRow The index of the first row in the range
39677      * @param {Number} endRow The index of the last row in the range
39678      * @param {Boolean} keepExisting (optional) True to retain existing selections
39679      */
39680     selectRange : function(startRow, endRow, keepExisting){
39681         if(this.locked) return;
39682         if(!keepExisting){
39683             this.clearSelections();
39684         }
39685         if(startRow <= endRow){
39686             for(var i = startRow; i <= endRow; i++){
39687                 this.selectRow(i, true);
39688             }
39689         }else{
39690             for(var i = startRow; i >= endRow; i--){
39691                 this.selectRow(i, true);
39692             }
39693         }
39694     },
39695
39696     /**
39697      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39698      * @param {Number} startRow The index of the first row in the range
39699      * @param {Number} endRow The index of the last row in the range
39700      */
39701     deselectRange : function(startRow, endRow, preventViewNotify){
39702         if(this.locked) return;
39703         for(var i = startRow; i <= endRow; i++){
39704             this.deselectRow(i, preventViewNotify);
39705         }
39706     },
39707
39708     /**
39709      * Selects a row.
39710      * @param {Number} row The index of the row to select
39711      * @param {Boolean} keepExisting (optional) True to keep existing selections
39712      */
39713     selectRow : function(index, keepExisting, preventViewNotify){
39714         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39715         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39716             if(!keepExisting || this.singleSelect){
39717                 this.clearSelections();
39718             }
39719             var r = this.grid.dataSource.getAt(index);
39720             this.selections.add(r);
39721             this.last = this.lastActive = index;
39722             if(!preventViewNotify){
39723                 this.grid.getView().onRowSelect(index);
39724             }
39725             this.fireEvent("rowselect", this, index, r);
39726             this.fireEvent("selectionchange", this);
39727         }
39728     },
39729
39730     /**
39731      * Deselects a row.
39732      * @param {Number} row The index of the row to deselect
39733      */
39734     deselectRow : function(index, preventViewNotify){
39735         if(this.locked) return;
39736         if(this.last == index){
39737             this.last = false;
39738         }
39739         if(this.lastActive == index){
39740             this.lastActive = false;
39741         }
39742         var r = this.grid.dataSource.getAt(index);
39743         this.selections.remove(r);
39744         if(!preventViewNotify){
39745             this.grid.getView().onRowDeselect(index);
39746         }
39747         this.fireEvent("rowdeselect", this, index);
39748         this.fireEvent("selectionchange", this);
39749     },
39750
39751     // private
39752     restoreLast : function(){
39753         if(this._last){
39754             this.last = this._last;
39755         }
39756     },
39757
39758     // private
39759     acceptsNav : function(row, col, cm){
39760         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39761     },
39762
39763     // private
39764     onEditorKey : function(field, e){
39765         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39766         if(k == e.TAB){
39767             e.stopEvent();
39768             ed.completeEdit();
39769             if(e.shiftKey){
39770                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39771             }else{
39772                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39773             }
39774         }else if(k == e.ENTER && !e.ctrlKey){
39775             e.stopEvent();
39776             ed.completeEdit();
39777             if(e.shiftKey){
39778                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39779             }else{
39780                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39781             }
39782         }else if(k == e.ESC){
39783             ed.cancelEdit();
39784         }
39785         if(newCell){
39786             g.startEditing(newCell[0], newCell[1]);
39787         }
39788     }
39789 });/*
39790  * Based on:
39791  * Ext JS Library 1.1.1
39792  * Copyright(c) 2006-2007, Ext JS, LLC.
39793  *
39794  * Originally Released Under LGPL - original licence link has changed is not relivant.
39795  *
39796  * Fork - LGPL
39797  * <script type="text/javascript">
39798  */
39799 /**
39800  * @class Roo.grid.CellSelectionModel
39801  * @extends Roo.grid.AbstractSelectionModel
39802  * This class provides the basic implementation for cell selection in a grid.
39803  * @constructor
39804  * @param {Object} config The object containing the configuration of this model.
39805  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39806  */
39807 Roo.grid.CellSelectionModel = function(config){
39808     Roo.apply(this, config);
39809
39810     this.selection = null;
39811
39812     this.addEvents({
39813         /**
39814              * @event beforerowselect
39815              * Fires before a cell is selected.
39816              * @param {SelectionModel} this
39817              * @param {Number} rowIndex The selected row index
39818              * @param {Number} colIndex The selected cell index
39819              */
39820             "beforecellselect" : true,
39821         /**
39822              * @event cellselect
39823              * Fires when a cell is selected.
39824              * @param {SelectionModel} this
39825              * @param {Number} rowIndex The selected row index
39826              * @param {Number} colIndex The selected cell index
39827              */
39828             "cellselect" : true,
39829         /**
39830              * @event selectionchange
39831              * Fires when the active selection changes.
39832              * @param {SelectionModel} this
39833              * @param {Object} selection null for no selection or an object (o) with two properties
39834                 <ul>
39835                 <li>o.record: the record object for the row the selection is in</li>
39836                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39837                 </ul>
39838              */
39839             "selectionchange" : true,
39840         /**
39841              * @event tabend
39842              * Fires when the tab (or enter) was pressed on the last editable cell
39843              * You can use this to trigger add new row.
39844              * @param {SelectionModel} this
39845              */
39846             "tabend" : true,
39847          /**
39848              * @event beforeeditnext
39849              * Fires before the next editable sell is made active
39850              * You can use this to skip to another cell or fire the tabend
39851              *    if you set cell to false
39852              * @param {Object} eventdata object : { cell : [ row, col ] } 
39853              */
39854             "beforeeditnext" : true
39855     });
39856     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39857 };
39858
39859 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39860     
39861     enter_is_tab: false,
39862
39863     /** @ignore */
39864     initEvents : function(){
39865         this.grid.on("mousedown", this.handleMouseDown, this);
39866         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39867         var view = this.grid.view;
39868         view.on("refresh", this.onViewChange, this);
39869         view.on("rowupdated", this.onRowUpdated, this);
39870         view.on("beforerowremoved", this.clearSelections, this);
39871         view.on("beforerowsinserted", this.clearSelections, this);
39872         if(this.grid.isEditor){
39873             this.grid.on("beforeedit", this.beforeEdit,  this);
39874         }
39875     },
39876
39877         //private
39878     beforeEdit : function(e){
39879         this.select(e.row, e.column, false, true, e.record);
39880     },
39881
39882         //private
39883     onRowUpdated : function(v, index, r){
39884         if(this.selection && this.selection.record == r){
39885             v.onCellSelect(index, this.selection.cell[1]);
39886         }
39887     },
39888
39889         //private
39890     onViewChange : function(){
39891         this.clearSelections(true);
39892     },
39893
39894         /**
39895          * Returns the currently selected cell,.
39896          * @return {Array} The selected cell (row, column) or null if none selected.
39897          */
39898     getSelectedCell : function(){
39899         return this.selection ? this.selection.cell : null;
39900     },
39901
39902     /**
39903      * Clears all selections.
39904      * @param {Boolean} true to prevent the gridview from being notified about the change.
39905      */
39906     clearSelections : function(preventNotify){
39907         var s = this.selection;
39908         if(s){
39909             if(preventNotify !== true){
39910                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39911             }
39912             this.selection = null;
39913             this.fireEvent("selectionchange", this, null);
39914         }
39915     },
39916
39917     /**
39918      * Returns true if there is a selection.
39919      * @return {Boolean}
39920      */
39921     hasSelection : function(){
39922         return this.selection ? true : false;
39923     },
39924
39925     /** @ignore */
39926     handleMouseDown : function(e, t){
39927         var v = this.grid.getView();
39928         if(this.isLocked()){
39929             return;
39930         };
39931         var row = v.findRowIndex(t);
39932         var cell = v.findCellIndex(t);
39933         if(row !== false && cell !== false){
39934             this.select(row, cell);
39935         }
39936     },
39937
39938     /**
39939      * Selects a cell.
39940      * @param {Number} rowIndex
39941      * @param {Number} collIndex
39942      */
39943     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39944         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39945             this.clearSelections();
39946             r = r || this.grid.dataSource.getAt(rowIndex);
39947             this.selection = {
39948                 record : r,
39949                 cell : [rowIndex, colIndex]
39950             };
39951             if(!preventViewNotify){
39952                 var v = this.grid.getView();
39953                 v.onCellSelect(rowIndex, colIndex);
39954                 if(preventFocus !== true){
39955                     v.focusCell(rowIndex, colIndex);
39956                 }
39957             }
39958             this.fireEvent("cellselect", this, rowIndex, colIndex);
39959             this.fireEvent("selectionchange", this, this.selection);
39960         }
39961     },
39962
39963         //private
39964     isSelectable : function(rowIndex, colIndex, cm){
39965         return !cm.isHidden(colIndex);
39966     },
39967
39968     /** @ignore */
39969     handleKeyDown : function(e){
39970         //Roo.log('Cell Sel Model handleKeyDown');
39971         if(!e.isNavKeyPress()){
39972             return;
39973         }
39974         var g = this.grid, s = this.selection;
39975         if(!s){
39976             e.stopEvent();
39977             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39978             if(cell){
39979                 this.select(cell[0], cell[1]);
39980             }
39981             return;
39982         }
39983         var sm = this;
39984         var walk = function(row, col, step){
39985             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39986         };
39987         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39988         var newCell;
39989
39990       
39991
39992         switch(k){
39993             case e.TAB:
39994                 // handled by onEditorKey
39995                 if (g.isEditor && g.editing) {
39996                     return;
39997                 }
39998                 if(e.shiftKey) {
39999                     newCell = walk(r, c-1, -1);
40000                 } else {
40001                     newCell = walk(r, c+1, 1);
40002                 }
40003                 break;
40004             
40005             case e.DOWN:
40006                newCell = walk(r+1, c, 1);
40007                 break;
40008             
40009             case e.UP:
40010                 newCell = walk(r-1, c, -1);
40011                 break;
40012             
40013             case e.RIGHT:
40014                 newCell = walk(r, c+1, 1);
40015                 break;
40016             
40017             case e.LEFT:
40018                 newCell = walk(r, c-1, -1);
40019                 break;
40020             
40021             case e.ENTER:
40022                 
40023                 if(g.isEditor && !g.editing){
40024                    g.startEditing(r, c);
40025                    e.stopEvent();
40026                    return;
40027                 }
40028                 
40029                 
40030              break;
40031         };
40032         if(newCell){
40033             this.select(newCell[0], newCell[1]);
40034             e.stopEvent();
40035             
40036         }
40037     },
40038
40039     acceptsNav : function(row, col, cm){
40040         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40041     },
40042     /**
40043      * Selects a cell.
40044      * @param {Number} field (not used) - as it's normally used as a listener
40045      * @param {Number} e - event - fake it by using
40046      *
40047      * var e = Roo.EventObjectImpl.prototype;
40048      * e.keyCode = e.TAB
40049      *
40050      * 
40051      */
40052     onEditorKey : function(field, e){
40053         
40054         var k = e.getKey(),
40055             newCell,
40056             g = this.grid,
40057             ed = g.activeEditor,
40058             forward = false;
40059         ///Roo.log('onEditorKey' + k);
40060         
40061         
40062         if (this.enter_is_tab && k == e.ENTER) {
40063             k = e.TAB;
40064         }
40065         
40066         if(k == e.TAB){
40067             if(e.shiftKey){
40068                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40069             }else{
40070                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40071                 forward = true;
40072             }
40073             
40074             e.stopEvent();
40075             
40076         } else if(k == e.ENTER &&  !e.ctrlKey){
40077             ed.completeEdit();
40078             e.stopEvent();
40079             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40080         
40081                 } else if(k == e.ESC){
40082             ed.cancelEdit();
40083         }
40084                 
40085         if (newCell) {
40086             var ecall = { cell : newCell, forward : forward };
40087             this.fireEvent('beforeeditnext', ecall );
40088             newCell = ecall.cell;
40089                         forward = ecall.forward;
40090         }
40091                 
40092         if(newCell){
40093             //Roo.log('next cell after edit');
40094             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40095         } else if (forward) {
40096             // tabbed past last
40097             this.fireEvent.defer(100, this, ['tabend',this]);
40098         }
40099     }
40100 });/*
40101  * Based on:
40102  * Ext JS Library 1.1.1
40103  * Copyright(c) 2006-2007, Ext JS, LLC.
40104  *
40105  * Originally Released Under LGPL - original licence link has changed is not relivant.
40106  *
40107  * Fork - LGPL
40108  * <script type="text/javascript">
40109  */
40110  
40111 /**
40112  * @class Roo.grid.EditorGrid
40113  * @extends Roo.grid.Grid
40114  * Class for creating and editable grid.
40115  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40116  * The container MUST have some type of size defined for the grid to fill. The container will be 
40117  * automatically set to position relative if it isn't already.
40118  * @param {Object} dataSource The data model to bind to
40119  * @param {Object} colModel The column model with info about this grid's columns
40120  */
40121 Roo.grid.EditorGrid = function(container, config){
40122     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40123     this.getGridEl().addClass("xedit-grid");
40124
40125     if(!this.selModel){
40126         this.selModel = new Roo.grid.CellSelectionModel();
40127     }
40128
40129     this.activeEditor = null;
40130
40131         this.addEvents({
40132             /**
40133              * @event beforeedit
40134              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40135              * <ul style="padding:5px;padding-left:16px;">
40136              * <li>grid - This grid</li>
40137              * <li>record - The record being edited</li>
40138              * <li>field - The field name being edited</li>
40139              * <li>value - The value for the field being edited.</li>
40140              * <li>row - The grid row index</li>
40141              * <li>column - The grid column index</li>
40142              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40143              * </ul>
40144              * @param {Object} e An edit event (see above for description)
40145              */
40146             "beforeedit" : true,
40147             /**
40148              * @event afteredit
40149              * Fires after a cell is edited. <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 being set</li>
40155              * <li>originalValue - The original value for the field, before the edit.</li>
40156              * <li>row - The grid row index</li>
40157              * <li>column - The grid column index</li>
40158              * </ul>
40159              * @param {Object} e An edit event (see above for description)
40160              */
40161             "afteredit" : true,
40162             /**
40163              * @event validateedit
40164              * Fires after a cell is edited, but before the value is set in the record. 
40165          * You can use this to modify the value being set in the field, Return false
40166              * to cancel the change. The edit event object has the following properties <br />
40167              * <ul style="padding:5px;padding-left:16px;">
40168          * <li>editor - This editor</li>
40169              * <li>grid - This grid</li>
40170              * <li>record - The record being edited</li>
40171              * <li>field - The field name being edited</li>
40172              * <li>value - The value being set</li>
40173              * <li>originalValue - The original value for the field, before the edit.</li>
40174              * <li>row - The grid row index</li>
40175              * <li>column - The grid column index</li>
40176              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40177              * </ul>
40178              * @param {Object} e An edit event (see above for description)
40179              */
40180             "validateedit" : true
40181         });
40182     this.on("bodyscroll", this.stopEditing,  this);
40183     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40184 };
40185
40186 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40187     /**
40188      * @cfg {Number} clicksToEdit
40189      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40190      */
40191     clicksToEdit: 2,
40192
40193     // private
40194     isEditor : true,
40195     // private
40196     trackMouseOver: false, // causes very odd FF errors
40197
40198     onCellDblClick : function(g, row, col){
40199         this.startEditing(row, col);
40200     },
40201
40202     onEditComplete : function(ed, value, startValue){
40203         this.editing = false;
40204         this.activeEditor = null;
40205         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40206         var r = ed.record;
40207         var field = this.colModel.getDataIndex(ed.col);
40208         var e = {
40209             grid: this,
40210             record: r,
40211             field: field,
40212             originalValue: startValue,
40213             value: value,
40214             row: ed.row,
40215             column: ed.col,
40216             cancel:false,
40217             editor: ed
40218         };
40219         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
40220         cell.show();
40221           
40222         if(String(value) !== String(startValue)){
40223             
40224             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40225                 r.set(field, e.value);
40226                 // if we are dealing with a combo box..
40227                 // then we also set the 'name' colum to be the displayField
40228                 if (ed.field.displayField && ed.field.name) {
40229                     r.set(ed.field.name, ed.field.el.dom.value);
40230                 }
40231                 
40232                 delete e.cancel; //?? why!!!
40233                 this.fireEvent("afteredit", e);
40234             }
40235         } else {
40236             this.fireEvent("afteredit", e); // always fire it!
40237         }
40238         this.view.focusCell(ed.row, ed.col);
40239     },
40240
40241     /**
40242      * Starts editing the specified for the specified row/column
40243      * @param {Number} rowIndex
40244      * @param {Number} colIndex
40245      */
40246     startEditing : function(row, col){
40247         this.stopEditing();
40248         if(this.colModel.isCellEditable(col, row)){
40249             this.view.ensureVisible(row, col, true);
40250           
40251             var r = this.dataSource.getAt(row);
40252             var field = this.colModel.getDataIndex(col);
40253             var cell = Roo.get(this.view.getCell(row,col));
40254             var e = {
40255                 grid: this,
40256                 record: r,
40257                 field: field,
40258                 value: r.data[field],
40259                 row: row,
40260                 column: col,
40261                 cancel:false 
40262             };
40263             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40264                 this.editing = true;
40265                 var ed = this.colModel.getCellEditor(col, row);
40266                 
40267                 if (!ed) {
40268                     return;
40269                 }
40270                 if(!ed.rendered){
40271                     ed.render(ed.parentEl || document.body);
40272                 }
40273                 ed.field.reset();
40274                
40275                 cell.hide();
40276                 
40277                 (function(){ // complex but required for focus issues in safari, ie and opera
40278                     ed.row = row;
40279                     ed.col = col;
40280                     ed.record = r;
40281                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40282                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40283                     this.activeEditor = ed;
40284                     var v = r.data[field];
40285                     ed.startEdit(this.view.getCell(row, col), v);
40286                     // combo's with 'displayField and name set
40287                     if (ed.field.displayField && ed.field.name) {
40288                         ed.field.el.dom.value = r.data[ed.field.name];
40289                     }
40290                     
40291                     
40292                 }).defer(50, this);
40293             }
40294         }
40295     },
40296         
40297     /**
40298      * Stops any active editing
40299      */
40300     stopEditing : function(){
40301         if(this.activeEditor){
40302             this.activeEditor.completeEdit();
40303         }
40304         this.activeEditor = null;
40305     },
40306         
40307          /**
40308      * Called to get grid's drag proxy text, by default returns this.ddText.
40309      * @return {String}
40310      */
40311     getDragDropText : function(){
40312         var count = this.selModel.getSelectedCell() ? 1 : 0;
40313         return String.format(this.ddText, count, count == 1 ? '' : 's');
40314     }
40315         
40316 });/*
40317  * Based on:
40318  * Ext JS Library 1.1.1
40319  * Copyright(c) 2006-2007, Ext JS, LLC.
40320  *
40321  * Originally Released Under LGPL - original licence link has changed is not relivant.
40322  *
40323  * Fork - LGPL
40324  * <script type="text/javascript">
40325  */
40326
40327 // private - not really -- you end up using it !
40328 // This is a support class used internally by the Grid components
40329
40330 /**
40331  * @class Roo.grid.GridEditor
40332  * @extends Roo.Editor
40333  * Class for creating and editable grid elements.
40334  * @param {Object} config any settings (must include field)
40335  */
40336 Roo.grid.GridEditor = function(field, config){
40337     if (!config && field.field) {
40338         config = field;
40339         field = Roo.factory(config.field, Roo.form);
40340     }
40341     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40342     field.monitorTab = false;
40343 };
40344
40345 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40346     
40347     /**
40348      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40349      */
40350     
40351     alignment: "tl-tl",
40352     autoSize: "width",
40353     hideEl : false,
40354     cls: "x-small-editor x-grid-editor",
40355     shim:false,
40356     shadow:"frame"
40357 });/*
40358  * Based on:
40359  * Ext JS Library 1.1.1
40360  * Copyright(c) 2006-2007, Ext JS, LLC.
40361  *
40362  * Originally Released Under LGPL - original licence link has changed is not relivant.
40363  *
40364  * Fork - LGPL
40365  * <script type="text/javascript">
40366  */
40367   
40368
40369   
40370 Roo.grid.PropertyRecord = Roo.data.Record.create([
40371     {name:'name',type:'string'},  'value'
40372 ]);
40373
40374
40375 Roo.grid.PropertyStore = function(grid, source){
40376     this.grid = grid;
40377     this.store = new Roo.data.Store({
40378         recordType : Roo.grid.PropertyRecord
40379     });
40380     this.store.on('update', this.onUpdate,  this);
40381     if(source){
40382         this.setSource(source);
40383     }
40384     Roo.grid.PropertyStore.superclass.constructor.call(this);
40385 };
40386
40387
40388
40389 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40390     setSource : function(o){
40391         this.source = o;
40392         this.store.removeAll();
40393         var data = [];
40394         for(var k in o){
40395             if(this.isEditableValue(o[k])){
40396                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40397             }
40398         }
40399         this.store.loadRecords({records: data}, {}, true);
40400     },
40401
40402     onUpdate : function(ds, record, type){
40403         if(type == Roo.data.Record.EDIT){
40404             var v = record.data['value'];
40405             var oldValue = record.modified['value'];
40406             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40407                 this.source[record.id] = v;
40408                 record.commit();
40409                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40410             }else{
40411                 record.reject();
40412             }
40413         }
40414     },
40415
40416     getProperty : function(row){
40417        return this.store.getAt(row);
40418     },
40419
40420     isEditableValue: function(val){
40421         if(val && val instanceof Date){
40422             return true;
40423         }else if(typeof val == 'object' || typeof val == 'function'){
40424             return false;
40425         }
40426         return true;
40427     },
40428
40429     setValue : function(prop, value){
40430         this.source[prop] = value;
40431         this.store.getById(prop).set('value', value);
40432     },
40433
40434     getSource : function(){
40435         return this.source;
40436     }
40437 });
40438
40439 Roo.grid.PropertyColumnModel = function(grid, store){
40440     this.grid = grid;
40441     var g = Roo.grid;
40442     g.PropertyColumnModel.superclass.constructor.call(this, [
40443         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40444         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40445     ]);
40446     this.store = store;
40447     this.bselect = Roo.DomHelper.append(document.body, {
40448         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40449             {tag: 'option', value: 'true', html: 'true'},
40450             {tag: 'option', value: 'false', html: 'false'}
40451         ]
40452     });
40453     Roo.id(this.bselect);
40454     var f = Roo.form;
40455     this.editors = {
40456         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40457         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40458         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40459         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40460         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40461     };
40462     this.renderCellDelegate = this.renderCell.createDelegate(this);
40463     this.renderPropDelegate = this.renderProp.createDelegate(this);
40464 };
40465
40466 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40467     
40468     
40469     nameText : 'Name',
40470     valueText : 'Value',
40471     
40472     dateFormat : 'm/j/Y',
40473     
40474     
40475     renderDate : function(dateVal){
40476         return dateVal.dateFormat(this.dateFormat);
40477     },
40478
40479     renderBool : function(bVal){
40480         return bVal ? 'true' : 'false';
40481     },
40482
40483     isCellEditable : function(colIndex, rowIndex){
40484         return colIndex == 1;
40485     },
40486
40487     getRenderer : function(col){
40488         return col == 1 ?
40489             this.renderCellDelegate : this.renderPropDelegate;
40490     },
40491
40492     renderProp : function(v){
40493         return this.getPropertyName(v);
40494     },
40495
40496     renderCell : function(val){
40497         var rv = val;
40498         if(val instanceof Date){
40499             rv = this.renderDate(val);
40500         }else if(typeof val == 'boolean'){
40501             rv = this.renderBool(val);
40502         }
40503         return Roo.util.Format.htmlEncode(rv);
40504     },
40505
40506     getPropertyName : function(name){
40507         var pn = this.grid.propertyNames;
40508         return pn && pn[name] ? pn[name] : name;
40509     },
40510
40511     getCellEditor : function(colIndex, rowIndex){
40512         var p = this.store.getProperty(rowIndex);
40513         var n = p.data['name'], val = p.data['value'];
40514         
40515         if(typeof(this.grid.customEditors[n]) == 'string'){
40516             return this.editors[this.grid.customEditors[n]];
40517         }
40518         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40519             return this.grid.customEditors[n];
40520         }
40521         if(val instanceof Date){
40522             return this.editors['date'];
40523         }else if(typeof val == 'number'){
40524             return this.editors['number'];
40525         }else if(typeof val == 'boolean'){
40526             return this.editors['boolean'];
40527         }else{
40528             return this.editors['string'];
40529         }
40530     }
40531 });
40532
40533 /**
40534  * @class Roo.grid.PropertyGrid
40535  * @extends Roo.grid.EditorGrid
40536  * This class represents the  interface of a component based property grid control.
40537  * <br><br>Usage:<pre><code>
40538  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40539       
40540  });
40541  // set any options
40542  grid.render();
40543  * </code></pre>
40544   
40545  * @constructor
40546  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40547  * The container MUST have some type of size defined for the grid to fill. The container will be
40548  * automatically set to position relative if it isn't already.
40549  * @param {Object} config A config object that sets properties on this grid.
40550  */
40551 Roo.grid.PropertyGrid = function(container, config){
40552     config = config || {};
40553     var store = new Roo.grid.PropertyStore(this);
40554     this.store = store;
40555     var cm = new Roo.grid.PropertyColumnModel(this, store);
40556     store.store.sort('name', 'ASC');
40557     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40558         ds: store.store,
40559         cm: cm,
40560         enableColLock:false,
40561         enableColumnMove:false,
40562         stripeRows:false,
40563         trackMouseOver: false,
40564         clicksToEdit:1
40565     }, config));
40566     this.getGridEl().addClass('x-props-grid');
40567     this.lastEditRow = null;
40568     this.on('columnresize', this.onColumnResize, this);
40569     this.addEvents({
40570          /**
40571              * @event beforepropertychange
40572              * Fires before a property changes (return false to stop?)
40573              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40574              * @param {String} id Record Id
40575              * @param {String} newval New Value
40576          * @param {String} oldval Old Value
40577              */
40578         "beforepropertychange": true,
40579         /**
40580              * @event propertychange
40581              * Fires after a property changes
40582              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40583              * @param {String} id Record Id
40584              * @param {String} newval New Value
40585          * @param {String} oldval Old Value
40586              */
40587         "propertychange": true
40588     });
40589     this.customEditors = this.customEditors || {};
40590 };
40591 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40592     
40593      /**
40594      * @cfg {Object} customEditors map of colnames=> custom editors.
40595      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40596      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40597      * false disables editing of the field.
40598          */
40599     
40600       /**
40601      * @cfg {Object} propertyNames map of property Names to their displayed value
40602          */
40603     
40604     render : function(){
40605         Roo.grid.PropertyGrid.superclass.render.call(this);
40606         this.autoSize.defer(100, this);
40607     },
40608
40609     autoSize : function(){
40610         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40611         if(this.view){
40612             this.view.fitColumns();
40613         }
40614     },
40615
40616     onColumnResize : function(){
40617         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40618         this.autoSize();
40619     },
40620     /**
40621      * Sets the data for the Grid
40622      * accepts a Key => Value object of all the elements avaiable.
40623      * @param {Object} data  to appear in grid.
40624      */
40625     setSource : function(source){
40626         this.store.setSource(source);
40627         //this.autoSize();
40628     },
40629     /**
40630      * Gets all the data from the grid.
40631      * @return {Object} data  data stored in grid
40632      */
40633     getSource : function(){
40634         return this.store.getSource();
40635     }
40636 });/*
40637   
40638  * Licence LGPL
40639  
40640  */
40641  
40642 /**
40643  * @class Roo.grid.Calendar
40644  * @extends Roo.util.Grid
40645  * This class extends the Grid to provide a calendar widget
40646  * <br><br>Usage:<pre><code>
40647  var grid = new Roo.grid.Calendar("my-container-id", {
40648      ds: myDataStore,
40649      cm: myColModel,
40650      selModel: mySelectionModel,
40651      autoSizeColumns: true,
40652      monitorWindowResize: false,
40653      trackMouseOver: true
40654      eventstore : real data store..
40655  });
40656  // set any options
40657  grid.render();
40658   
40659   * @constructor
40660  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40661  * The container MUST have some type of size defined for the grid to fill. The container will be
40662  * automatically set to position relative if it isn't already.
40663  * @param {Object} config A config object that sets properties on this grid.
40664  */
40665 Roo.grid.Calendar = function(container, config){
40666         // initialize the container
40667         this.container = Roo.get(container);
40668         this.container.update("");
40669         this.container.setStyle("overflow", "hidden");
40670     this.container.addClass('x-grid-container');
40671
40672     this.id = this.container.id;
40673
40674     Roo.apply(this, config);
40675     // check and correct shorthanded configs
40676     
40677     var rows = [];
40678     var d =1;
40679     for (var r = 0;r < 6;r++) {
40680         
40681         rows[r]=[];
40682         for (var c =0;c < 7;c++) {
40683             rows[r][c]= '';
40684         }
40685     }
40686     if (this.eventStore) {
40687         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40688         this.eventStore.on('load',this.onLoad, this);
40689         this.eventStore.on('beforeload',this.clearEvents, this);
40690          
40691     }
40692     
40693     this.dataSource = new Roo.data.Store({
40694             proxy: new Roo.data.MemoryProxy(rows),
40695             reader: new Roo.data.ArrayReader({}, [
40696                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40697     });
40698
40699     this.dataSource.load();
40700     this.ds = this.dataSource;
40701     this.ds.xmodule = this.xmodule || false;
40702     
40703     
40704     var cellRender = function(v,x,r)
40705     {
40706         return String.format(
40707             '<div class="fc-day  fc-widget-content"><div>' +
40708                 '<div class="fc-event-container"></div>' +
40709                 '<div class="fc-day-number">{0}</div>'+
40710                 
40711                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40712             '</div></div>', v);
40713     
40714     }
40715     
40716     
40717     this.colModel = new Roo.grid.ColumnModel( [
40718         {
40719             xtype: 'ColumnModel',
40720             xns: Roo.grid,
40721             dataIndex : 'weekday0',
40722             header : 'Sunday',
40723             renderer : cellRender
40724         },
40725         {
40726             xtype: 'ColumnModel',
40727             xns: Roo.grid,
40728             dataIndex : 'weekday1',
40729             header : 'Monday',
40730             renderer : cellRender
40731         },
40732         {
40733             xtype: 'ColumnModel',
40734             xns: Roo.grid,
40735             dataIndex : 'weekday2',
40736             header : 'Tuesday',
40737             renderer : cellRender
40738         },
40739         {
40740             xtype: 'ColumnModel',
40741             xns: Roo.grid,
40742             dataIndex : 'weekday3',
40743             header : 'Wednesday',
40744             renderer : cellRender
40745         },
40746         {
40747             xtype: 'ColumnModel',
40748             xns: Roo.grid,
40749             dataIndex : 'weekday4',
40750             header : 'Thursday',
40751             renderer : cellRender
40752         },
40753         {
40754             xtype: 'ColumnModel',
40755             xns: Roo.grid,
40756             dataIndex : 'weekday5',
40757             header : 'Friday',
40758             renderer : cellRender
40759         },
40760         {
40761             xtype: 'ColumnModel',
40762             xns: Roo.grid,
40763             dataIndex : 'weekday6',
40764             header : 'Saturday',
40765             renderer : cellRender
40766         }
40767     ]);
40768     this.cm = this.colModel;
40769     this.cm.xmodule = this.xmodule || false;
40770  
40771         
40772           
40773     //this.selModel = new Roo.grid.CellSelectionModel();
40774     //this.sm = this.selModel;
40775     //this.selModel.init(this);
40776     
40777     
40778     if(this.width){
40779         this.container.setWidth(this.width);
40780     }
40781
40782     if(this.height){
40783         this.container.setHeight(this.height);
40784     }
40785     /** @private */
40786         this.addEvents({
40787         // raw events
40788         /**
40789          * @event click
40790          * The raw click event for the entire grid.
40791          * @param {Roo.EventObject} e
40792          */
40793         "click" : true,
40794         /**
40795          * @event dblclick
40796          * The raw dblclick event for the entire grid.
40797          * @param {Roo.EventObject} e
40798          */
40799         "dblclick" : true,
40800         /**
40801          * @event contextmenu
40802          * The raw contextmenu event for the entire grid.
40803          * @param {Roo.EventObject} e
40804          */
40805         "contextmenu" : true,
40806         /**
40807          * @event mousedown
40808          * The raw mousedown event for the entire grid.
40809          * @param {Roo.EventObject} e
40810          */
40811         "mousedown" : true,
40812         /**
40813          * @event mouseup
40814          * The raw mouseup event for the entire grid.
40815          * @param {Roo.EventObject} e
40816          */
40817         "mouseup" : true,
40818         /**
40819          * @event mouseover
40820          * The raw mouseover event for the entire grid.
40821          * @param {Roo.EventObject} e
40822          */
40823         "mouseover" : true,
40824         /**
40825          * @event mouseout
40826          * The raw mouseout event for the entire grid.
40827          * @param {Roo.EventObject} e
40828          */
40829         "mouseout" : true,
40830         /**
40831          * @event keypress
40832          * The raw keypress event for the entire grid.
40833          * @param {Roo.EventObject} e
40834          */
40835         "keypress" : true,
40836         /**
40837          * @event keydown
40838          * The raw keydown event for the entire grid.
40839          * @param {Roo.EventObject} e
40840          */
40841         "keydown" : true,
40842
40843         // custom events
40844
40845         /**
40846          * @event cellclick
40847          * Fires when a cell is clicked
40848          * @param {Grid} this
40849          * @param {Number} rowIndex
40850          * @param {Number} columnIndex
40851          * @param {Roo.EventObject} e
40852          */
40853         "cellclick" : true,
40854         /**
40855          * @event celldblclick
40856          * Fires when a cell is double clicked
40857          * @param {Grid} this
40858          * @param {Number} rowIndex
40859          * @param {Number} columnIndex
40860          * @param {Roo.EventObject} e
40861          */
40862         "celldblclick" : true,
40863         /**
40864          * @event rowclick
40865          * Fires when a row is clicked
40866          * @param {Grid} this
40867          * @param {Number} rowIndex
40868          * @param {Roo.EventObject} e
40869          */
40870         "rowclick" : true,
40871         /**
40872          * @event rowdblclick
40873          * Fires when a row is double clicked
40874          * @param {Grid} this
40875          * @param {Number} rowIndex
40876          * @param {Roo.EventObject} e
40877          */
40878         "rowdblclick" : true,
40879         /**
40880          * @event headerclick
40881          * Fires when a header is clicked
40882          * @param {Grid} this
40883          * @param {Number} columnIndex
40884          * @param {Roo.EventObject} e
40885          */
40886         "headerclick" : true,
40887         /**
40888          * @event headerdblclick
40889          * Fires when a header cell is double clicked
40890          * @param {Grid} this
40891          * @param {Number} columnIndex
40892          * @param {Roo.EventObject} e
40893          */
40894         "headerdblclick" : true,
40895         /**
40896          * @event rowcontextmenu
40897          * Fires when a row is right clicked
40898          * @param {Grid} this
40899          * @param {Number} rowIndex
40900          * @param {Roo.EventObject} e
40901          */
40902         "rowcontextmenu" : true,
40903         /**
40904          * @event cellcontextmenu
40905          * Fires when a cell is right clicked
40906          * @param {Grid} this
40907          * @param {Number} rowIndex
40908          * @param {Number} cellIndex
40909          * @param {Roo.EventObject} e
40910          */
40911          "cellcontextmenu" : true,
40912         /**
40913          * @event headercontextmenu
40914          * Fires when a header is right clicked
40915          * @param {Grid} this
40916          * @param {Number} columnIndex
40917          * @param {Roo.EventObject} e
40918          */
40919         "headercontextmenu" : true,
40920         /**
40921          * @event bodyscroll
40922          * Fires when the body element is scrolled
40923          * @param {Number} scrollLeft
40924          * @param {Number} scrollTop
40925          */
40926         "bodyscroll" : true,
40927         /**
40928          * @event columnresize
40929          * Fires when the user resizes a column
40930          * @param {Number} columnIndex
40931          * @param {Number} newSize
40932          */
40933         "columnresize" : true,
40934         /**
40935          * @event columnmove
40936          * Fires when the user moves a column
40937          * @param {Number} oldIndex
40938          * @param {Number} newIndex
40939          */
40940         "columnmove" : true,
40941         /**
40942          * @event startdrag
40943          * Fires when row(s) start being dragged
40944          * @param {Grid} this
40945          * @param {Roo.GridDD} dd The drag drop object
40946          * @param {event} e The raw browser event
40947          */
40948         "startdrag" : true,
40949         /**
40950          * @event enddrag
40951          * Fires when a drag operation is complete
40952          * @param {Grid} this
40953          * @param {Roo.GridDD} dd The drag drop object
40954          * @param {event} e The raw browser event
40955          */
40956         "enddrag" : true,
40957         /**
40958          * @event dragdrop
40959          * Fires when dragged row(s) are dropped on a valid DD target
40960          * @param {Grid} this
40961          * @param {Roo.GridDD} dd The drag drop object
40962          * @param {String} targetId The target drag drop object
40963          * @param {event} e The raw browser event
40964          */
40965         "dragdrop" : true,
40966         /**
40967          * @event dragover
40968          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40969          * @param {Grid} this
40970          * @param {Roo.GridDD} dd The drag drop object
40971          * @param {String} targetId The target drag drop object
40972          * @param {event} e The raw browser event
40973          */
40974         "dragover" : true,
40975         /**
40976          * @event dragenter
40977          *  Fires when the dragged row(s) first cross another DD target while being dragged
40978          * @param {Grid} this
40979          * @param {Roo.GridDD} dd The drag drop object
40980          * @param {String} targetId The target drag drop object
40981          * @param {event} e The raw browser event
40982          */
40983         "dragenter" : true,
40984         /**
40985          * @event dragout
40986          * Fires when the dragged row(s) leave another DD target while being dragged
40987          * @param {Grid} this
40988          * @param {Roo.GridDD} dd The drag drop object
40989          * @param {String} targetId The target drag drop object
40990          * @param {event} e The raw browser event
40991          */
40992         "dragout" : true,
40993         /**
40994          * @event rowclass
40995          * Fires when a row is rendered, so you can change add a style to it.
40996          * @param {GridView} gridview   The grid view
40997          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40998          */
40999         'rowclass' : true,
41000
41001         /**
41002          * @event render
41003          * Fires when the grid is rendered
41004          * @param {Grid} grid
41005          */
41006         'render' : true,
41007             /**
41008              * @event select
41009              * Fires when a date is selected
41010              * @param {DatePicker} this
41011              * @param {Date} date The selected date
41012              */
41013         'select': true,
41014         /**
41015              * @event monthchange
41016              * Fires when the displayed month changes 
41017              * @param {DatePicker} this
41018              * @param {Date} date The selected month
41019              */
41020         'monthchange': true,
41021         /**
41022              * @event evententer
41023              * Fires when mouse over an event
41024              * @param {Calendar} this
41025              * @param {event} Event
41026              */
41027         'evententer': true,
41028         /**
41029              * @event eventleave
41030              * Fires when the mouse leaves an
41031              * @param {Calendar} this
41032              * @param {event}
41033              */
41034         'eventleave': true,
41035         /**
41036              * @event eventclick
41037              * Fires when the mouse click an
41038              * @param {Calendar} this
41039              * @param {event}
41040              */
41041         'eventclick': true,
41042         /**
41043              * @event eventrender
41044              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41045              * @param {Calendar} this
41046              * @param {data} data to be modified
41047              */
41048         'eventrender': true
41049         
41050     });
41051
41052     Roo.grid.Grid.superclass.constructor.call(this);
41053     this.on('render', function() {
41054         this.view.el.addClass('x-grid-cal'); 
41055         
41056         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41057
41058     },this);
41059     
41060     if (!Roo.grid.Calendar.style) {
41061         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41062             
41063             
41064             '.x-grid-cal .x-grid-col' :  {
41065                 height: 'auto !important',
41066                 'vertical-align': 'top'
41067             },
41068             '.x-grid-cal  .fc-event-hori' : {
41069                 height: '14px'
41070             }
41071              
41072             
41073         }, Roo.id());
41074     }
41075
41076     
41077     
41078 };
41079 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41080     /**
41081      * @cfg {Store} eventStore The store that loads events.
41082      */
41083     eventStore : 25,
41084
41085      
41086     activeDate : false,
41087     startDay : 0,
41088     autoWidth : true,
41089     monitorWindowResize : false,
41090
41091     
41092     resizeColumns : function() {
41093         var col = (this.view.el.getWidth() / 7) - 3;
41094         // loop through cols, and setWidth
41095         for(var i =0 ; i < 7 ; i++){
41096             this.cm.setColumnWidth(i, col);
41097         }
41098     },
41099      setDate :function(date) {
41100         
41101         Roo.log('setDate?');
41102         
41103         this.resizeColumns();
41104         var vd = this.activeDate;
41105         this.activeDate = date;
41106 //        if(vd && this.el){
41107 //            var t = date.getTime();
41108 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41109 //                Roo.log('using add remove');
41110 //                
41111 //                this.fireEvent('monthchange', this, date);
41112 //                
41113 //                this.cells.removeClass("fc-state-highlight");
41114 //                this.cells.each(function(c){
41115 //                   if(c.dateValue == t){
41116 //                       c.addClass("fc-state-highlight");
41117 //                       setTimeout(function(){
41118 //                            try{c.dom.firstChild.focus();}catch(e){}
41119 //                       }, 50);
41120 //                       return false;
41121 //                   }
41122 //                   return true;
41123 //                });
41124 //                return;
41125 //            }
41126 //        }
41127         
41128         var days = date.getDaysInMonth();
41129         
41130         var firstOfMonth = date.getFirstDateOfMonth();
41131         var startingPos = firstOfMonth.getDay()-this.startDay;
41132         
41133         if(startingPos < this.startDay){
41134             startingPos += 7;
41135         }
41136         
41137         var pm = date.add(Date.MONTH, -1);
41138         var prevStart = pm.getDaysInMonth()-startingPos;
41139 //        
41140         
41141         
41142         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41143         
41144         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41145         //this.cells.addClassOnOver('fc-state-hover');
41146         
41147         var cells = this.cells.elements;
41148         var textEls = this.textNodes;
41149         
41150         //Roo.each(cells, function(cell){
41151         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41152         //});
41153         
41154         days += startingPos;
41155
41156         // convert everything to numbers so it's fast
41157         var day = 86400000;
41158         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41159         //Roo.log(d);
41160         //Roo.log(pm);
41161         //Roo.log(prevStart);
41162         
41163         var today = new Date().clearTime().getTime();
41164         var sel = date.clearTime().getTime();
41165         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41166         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41167         var ddMatch = this.disabledDatesRE;
41168         var ddText = this.disabledDatesText;
41169         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41170         var ddaysText = this.disabledDaysText;
41171         var format = this.format;
41172         
41173         var setCellClass = function(cal, cell){
41174             
41175             //Roo.log('set Cell Class');
41176             cell.title = "";
41177             var t = d.getTime();
41178             
41179             //Roo.log(d);
41180             
41181             
41182             cell.dateValue = t;
41183             if(t == today){
41184                 cell.className += " fc-today";
41185                 cell.className += " fc-state-highlight";
41186                 cell.title = cal.todayText;
41187             }
41188             if(t == sel){
41189                 // disable highlight in other month..
41190                 cell.className += " fc-state-highlight";
41191                 
41192             }
41193             // disabling
41194             if(t < min) {
41195                 //cell.className = " fc-state-disabled";
41196                 cell.title = cal.minText;
41197                 return;
41198             }
41199             if(t > max) {
41200                 //cell.className = " fc-state-disabled";
41201                 cell.title = cal.maxText;
41202                 return;
41203             }
41204             if(ddays){
41205                 if(ddays.indexOf(d.getDay()) != -1){
41206                     // cell.title = ddaysText;
41207                    // cell.className = " fc-state-disabled";
41208                 }
41209             }
41210             if(ddMatch && format){
41211                 var fvalue = d.dateFormat(format);
41212                 if(ddMatch.test(fvalue)){
41213                     cell.title = ddText.replace("%0", fvalue);
41214                    cell.className = " fc-state-disabled";
41215                 }
41216             }
41217             
41218             if (!cell.initialClassName) {
41219                 cell.initialClassName = cell.dom.className;
41220             }
41221             
41222             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41223         };
41224
41225         var i = 0;
41226         
41227         for(; i < startingPos; i++) {
41228             cells[i].dayName =  (++prevStart);
41229             Roo.log(textEls[i]);
41230             d.setDate(d.getDate()+1);
41231             
41232             //cells[i].className = "fc-past fc-other-month";
41233             setCellClass(this, cells[i]);
41234         }
41235         
41236         var intDay = 0;
41237         
41238         for(; i < days; i++){
41239             intDay = i - startingPos + 1;
41240             cells[i].dayName =  (intDay);
41241             d.setDate(d.getDate()+1);
41242             
41243             cells[i].className = ''; // "x-date-active";
41244             setCellClass(this, cells[i]);
41245         }
41246         var extraDays = 0;
41247         
41248         for(; i < 42; i++) {
41249             //textEls[i].innerHTML = (++extraDays);
41250             
41251             d.setDate(d.getDate()+1);
41252             cells[i].dayName = (++extraDays);
41253             cells[i].className = "fc-future fc-other-month";
41254             setCellClass(this, cells[i]);
41255         }
41256         
41257         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41258         
41259         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41260         
41261         // this will cause all the cells to mis
41262         var rows= [];
41263         var i =0;
41264         for (var r = 0;r < 6;r++) {
41265             for (var c =0;c < 7;c++) {
41266                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41267             }    
41268         }
41269         
41270         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41271         for(i=0;i<cells.length;i++) {
41272             
41273             this.cells.elements[i].dayName = cells[i].dayName ;
41274             this.cells.elements[i].className = cells[i].className;
41275             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41276             this.cells.elements[i].title = cells[i].title ;
41277             this.cells.elements[i].dateValue = cells[i].dateValue ;
41278         }
41279         
41280         
41281         
41282         
41283         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41284         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41285         
41286         ////if(totalRows != 6){
41287             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41288            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41289        // }
41290         
41291         this.fireEvent('monthchange', this, date);
41292         
41293         
41294     },
41295  /**
41296      * Returns the grid's SelectionModel.
41297      * @return {SelectionModel}
41298      */
41299     getSelectionModel : function(){
41300         if(!this.selModel){
41301             this.selModel = new Roo.grid.CellSelectionModel();
41302         }
41303         return this.selModel;
41304     },
41305
41306     load: function() {
41307         this.eventStore.load()
41308         
41309         
41310         
41311     },
41312     
41313     findCell : function(dt) {
41314         dt = dt.clearTime().getTime();
41315         var ret = false;
41316         this.cells.each(function(c){
41317             //Roo.log("check " +c.dateValue + '?=' + dt);
41318             if(c.dateValue == dt){
41319                 ret = c;
41320                 return false;
41321             }
41322             return true;
41323         });
41324         
41325         return ret;
41326     },
41327     
41328     findCells : function(rec) {
41329         var s = rec.data.start_dt.clone().clearTime().getTime();
41330        // Roo.log(s);
41331         var e= rec.data.end_dt.clone().clearTime().getTime();
41332        // Roo.log(e);
41333         var ret = [];
41334         this.cells.each(function(c){
41335              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41336             
41337             if(c.dateValue > e){
41338                 return ;
41339             }
41340             if(c.dateValue < s){
41341                 return ;
41342             }
41343             ret.push(c);
41344         });
41345         
41346         return ret;    
41347     },
41348     
41349     findBestRow: function(cells)
41350     {
41351         var ret = 0;
41352         
41353         for (var i =0 ; i < cells.length;i++) {
41354             ret  = Math.max(cells[i].rows || 0,ret);
41355         }
41356         return ret;
41357         
41358     },
41359     
41360     
41361     addItem : function(rec)
41362     {
41363         // look for vertical location slot in
41364         var cells = this.findCells(rec);
41365         
41366         rec.row = this.findBestRow(cells);
41367         
41368         // work out the location.
41369         
41370         var crow = false;
41371         var rows = [];
41372         for(var i =0; i < cells.length; i++) {
41373             if (!crow) {
41374                 crow = {
41375                     start : cells[i],
41376                     end :  cells[i]
41377                 };
41378                 continue;
41379             }
41380             if (crow.start.getY() == cells[i].getY()) {
41381                 // on same row.
41382                 crow.end = cells[i];
41383                 continue;
41384             }
41385             // different row.
41386             rows.push(crow);
41387             crow = {
41388                 start: cells[i],
41389                 end : cells[i]
41390             };
41391             
41392         }
41393         
41394         rows.push(crow);
41395         rec.els = [];
41396         rec.rows = rows;
41397         rec.cells = cells;
41398         for (var i = 0; i < cells.length;i++) {
41399             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41400             
41401         }
41402         
41403         
41404     },
41405     
41406     clearEvents: function() {
41407         
41408         if (!this.eventStore.getCount()) {
41409             return;
41410         }
41411         // reset number of rows in cells.
41412         Roo.each(this.cells.elements, function(c){
41413             c.rows = 0;
41414         });
41415         
41416         this.eventStore.each(function(e) {
41417             this.clearEvent(e);
41418         },this);
41419         
41420     },
41421     
41422     clearEvent : function(ev)
41423     {
41424         if (ev.els) {
41425             Roo.each(ev.els, function(el) {
41426                 el.un('mouseenter' ,this.onEventEnter, this);
41427                 el.un('mouseleave' ,this.onEventLeave, this);
41428                 el.remove();
41429             },this);
41430             ev.els = [];
41431         }
41432     },
41433     
41434     
41435     renderEvent : function(ev,ctr) {
41436         if (!ctr) {
41437              ctr = this.view.el.select('.fc-event-container',true).first();
41438         }
41439         
41440          
41441         this.clearEvent(ev);
41442             //code
41443        
41444         
41445         
41446         ev.els = [];
41447         var cells = ev.cells;
41448         var rows = ev.rows;
41449         this.fireEvent('eventrender', this, ev);
41450         
41451         for(var i =0; i < rows.length; i++) {
41452             
41453             cls = '';
41454             if (i == 0) {
41455                 cls += ' fc-event-start';
41456             }
41457             if ((i+1) == rows.length) {
41458                 cls += ' fc-event-end';
41459             }
41460             
41461             //Roo.log(ev.data);
41462             // how many rows should it span..
41463             var cg = this.eventTmpl.append(ctr,Roo.apply({
41464                 fccls : cls
41465                 
41466             }, ev.data) , true);
41467             
41468             
41469             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41470             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41471             cg.on('click', this.onEventClick, this, ev);
41472             
41473             ev.els.push(cg);
41474             
41475             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41476             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41477             //Roo.log(cg);
41478              
41479             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41480             cg.setWidth(ebox.right - sbox.x -2);
41481         }
41482     },
41483     
41484     renderEvents: function()
41485     {   
41486         // first make sure there is enough space..
41487         
41488         if (!this.eventTmpl) {
41489             this.eventTmpl = new Roo.Template(
41490                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41491                     '<div class="fc-event-inner">' +
41492                         '<span class="fc-event-time">{time}</span>' +
41493                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41494                     '</div>' +
41495                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41496                 '</div>'
41497             );
41498                 
41499         }
41500                
41501         
41502         
41503         this.cells.each(function(c) {
41504             //Roo.log(c.select('.fc-day-content div',true).first());
41505             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41506         });
41507         
41508         var ctr = this.view.el.select('.fc-event-container',true).first();
41509         
41510         var cls;
41511         this.eventStore.each(function(ev){
41512             
41513             this.renderEvent(ev);
41514              
41515              
41516         }, this);
41517         this.view.layout();
41518         
41519     },
41520     
41521     onEventEnter: function (e, el,event,d) {
41522         this.fireEvent('evententer', this, el, event);
41523     },
41524     
41525     onEventLeave: function (e, el,event,d) {
41526         this.fireEvent('eventleave', this, el, event);
41527     },
41528     
41529     onEventClick: function (e, el,event,d) {
41530         this.fireEvent('eventclick', this, el, event);
41531     },
41532     
41533     onMonthChange: function () {
41534         this.store.load();
41535     },
41536     
41537     onLoad: function () {
41538         
41539         //Roo.log('calendar onload');
41540 //         
41541         if(this.eventStore.getCount() > 0){
41542             
41543            
41544             
41545             this.eventStore.each(function(d){
41546                 
41547                 
41548                 // FIXME..
41549                 var add =   d.data;
41550                 if (typeof(add.end_dt) == 'undefined')  {
41551                     Roo.log("Missing End time in calendar data: ");
41552                     Roo.log(d);
41553                     return;
41554                 }
41555                 if (typeof(add.start_dt) == 'undefined')  {
41556                     Roo.log("Missing Start time in calendar data: ");
41557                     Roo.log(d);
41558                     return;
41559                 }
41560                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41561                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41562                 add.id = add.id || d.id;
41563                 add.title = add.title || '??';
41564                 
41565                 this.addItem(d);
41566                 
41567              
41568             },this);
41569         }
41570         
41571         this.renderEvents();
41572     }
41573     
41574
41575 });
41576 /*
41577  grid : {
41578                 xtype: 'Grid',
41579                 xns: Roo.grid,
41580                 listeners : {
41581                     render : function ()
41582                     {
41583                         _this.grid = this;
41584                         
41585                         if (!this.view.el.hasClass('course-timesheet')) {
41586                             this.view.el.addClass('course-timesheet');
41587                         }
41588                         if (this.tsStyle) {
41589                             this.ds.load({});
41590                             return; 
41591                         }
41592                         Roo.log('width');
41593                         Roo.log(_this.grid.view.el.getWidth());
41594                         
41595                         
41596                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41597                             '.course-timesheet .x-grid-row' : {
41598                                 height: '80px'
41599                             },
41600                             '.x-grid-row td' : {
41601                                 'vertical-align' : 0
41602                             },
41603                             '.course-edit-link' : {
41604                                 'color' : 'blue',
41605                                 'text-overflow' : 'ellipsis',
41606                                 'overflow' : 'hidden',
41607                                 'white-space' : 'nowrap',
41608                                 'cursor' : 'pointer'
41609                             },
41610                             '.sub-link' : {
41611                                 'color' : 'green'
41612                             },
41613                             '.de-act-sup-link' : {
41614                                 'color' : 'purple',
41615                                 'text-decoration' : 'line-through'
41616                             },
41617                             '.de-act-link' : {
41618                                 'color' : 'red',
41619                                 'text-decoration' : 'line-through'
41620                             },
41621                             '.course-timesheet .course-highlight' : {
41622                                 'border-top-style': 'dashed !important',
41623                                 'border-bottom-bottom': 'dashed !important'
41624                             },
41625                             '.course-timesheet .course-item' : {
41626                                 'font-family'   : 'tahoma, arial, helvetica',
41627                                 'font-size'     : '11px',
41628                                 'overflow'      : 'hidden',
41629                                 'padding-left'  : '10px',
41630                                 'padding-right' : '10px',
41631                                 'padding-top' : '10px' 
41632                             }
41633                             
41634                         }, Roo.id());
41635                                 this.ds.load({});
41636                     }
41637                 },
41638                 autoWidth : true,
41639                 monitorWindowResize : false,
41640                 cellrenderer : function(v,x,r)
41641                 {
41642                     return v;
41643                 },
41644                 sm : {
41645                     xtype: 'CellSelectionModel',
41646                     xns: Roo.grid
41647                 },
41648                 dataSource : {
41649                     xtype: 'Store',
41650                     xns: Roo.data,
41651                     listeners : {
41652                         beforeload : function (_self, options)
41653                         {
41654                             options.params = options.params || {};
41655                             options.params._month = _this.monthField.getValue();
41656                             options.params.limit = 9999;
41657                             options.params['sort'] = 'when_dt';    
41658                             options.params['dir'] = 'ASC';    
41659                             this.proxy.loadResponse = this.loadResponse;
41660                             Roo.log("load?");
41661                             //this.addColumns();
41662                         },
41663                         load : function (_self, records, options)
41664                         {
41665                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41666                                 // if you click on the translation.. you can edit it...
41667                                 var el = Roo.get(this);
41668                                 var id = el.dom.getAttribute('data-id');
41669                                 var d = el.dom.getAttribute('data-date');
41670                                 var t = el.dom.getAttribute('data-time');
41671                                 //var id = this.child('span').dom.textContent;
41672                                 
41673                                 //Roo.log(this);
41674                                 Pman.Dialog.CourseCalendar.show({
41675                                     id : id,
41676                                     when_d : d,
41677                                     when_t : t,
41678                                     productitem_active : id ? 1 : 0
41679                                 }, function() {
41680                                     _this.grid.ds.load({});
41681                                 });
41682                            
41683                            });
41684                            
41685                            _this.panel.fireEvent('resize', [ '', '' ]);
41686                         }
41687                     },
41688                     loadResponse : function(o, success, response){
41689                             // this is overridden on before load..
41690                             
41691                             Roo.log("our code?");       
41692                             //Roo.log(success);
41693                             //Roo.log(response)
41694                             delete this.activeRequest;
41695                             if(!success){
41696                                 this.fireEvent("loadexception", this, o, response);
41697                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41698                                 return;
41699                             }
41700                             var result;
41701                             try {
41702                                 result = o.reader.read(response);
41703                             }catch(e){
41704                                 Roo.log("load exception?");
41705                                 this.fireEvent("loadexception", this, o, response, e);
41706                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41707                                 return;
41708                             }
41709                             Roo.log("ready...");        
41710                             // loop through result.records;
41711                             // and set this.tdate[date] = [] << array of records..
41712                             _this.tdata  = {};
41713                             Roo.each(result.records, function(r){
41714                                 //Roo.log(r.data);
41715                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41716                                     _this.tdata[r.data.when_dt.format('j')] = [];
41717                                 }
41718                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41719                             });
41720                             
41721                             //Roo.log(_this.tdata);
41722                             
41723                             result.records = [];
41724                             result.totalRecords = 6;
41725                     
41726                             // let's generate some duumy records for the rows.
41727                             //var st = _this.dateField.getValue();
41728                             
41729                             // work out monday..
41730                             //st = st.add(Date.DAY, -1 * st.format('w'));
41731                             
41732                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41733                             
41734                             var firstOfMonth = date.getFirstDayOfMonth();
41735                             var days = date.getDaysInMonth();
41736                             var d = 1;
41737                             var firstAdded = false;
41738                             for (var i = 0; i < result.totalRecords ; i++) {
41739                                 //var d= st.add(Date.DAY, i);
41740                                 var row = {};
41741                                 var added = 0;
41742                                 for(var w = 0 ; w < 7 ; w++){
41743                                     if(!firstAdded && firstOfMonth != w){
41744                                         continue;
41745                                     }
41746                                     if(d > days){
41747                                         continue;
41748                                     }
41749                                     firstAdded = true;
41750                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41751                                     row['weekday'+w] = String.format(
41752                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41753                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41754                                                     d,
41755                                                     date.format('Y-m-')+dd
41756                                                 );
41757                                     added++;
41758                                     if(typeof(_this.tdata[d]) != 'undefined'){
41759                                         Roo.each(_this.tdata[d], function(r){
41760                                             var is_sub = '';
41761                                             var deactive = '';
41762                                             var id = r.id;
41763                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41764                                             if(r.parent_id*1>0){
41765                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41766                                                 id = r.parent_id;
41767                                             }
41768                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41769                                                 deactive = 'de-act-link';
41770                                             }
41771                                             
41772                                             row['weekday'+w] += String.format(
41773                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41774                                                     id, //0
41775                                                     r.product_id_name, //1
41776                                                     r.when_dt.format('h:ia'), //2
41777                                                     is_sub, //3
41778                                                     deactive, //4
41779                                                     desc // 5
41780                                             );
41781                                         });
41782                                     }
41783                                     d++;
41784                                 }
41785                                 
41786                                 // only do this if something added..
41787                                 if(added > 0){ 
41788                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41789                                 }
41790                                 
41791                                 
41792                                 // push it twice. (second one with an hour..
41793                                 
41794                             }
41795                             //Roo.log(result);
41796                             this.fireEvent("load", this, o, o.request.arg);
41797                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41798                         },
41799                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41800                     proxy : {
41801                         xtype: 'HttpProxy',
41802                         xns: Roo.data,
41803                         method : 'GET',
41804                         url : baseURL + '/Roo/Shop_course.php'
41805                     },
41806                     reader : {
41807                         xtype: 'JsonReader',
41808                         xns: Roo.data,
41809                         id : 'id',
41810                         fields : [
41811                             {
41812                                 'name': 'id',
41813                                 'type': 'int'
41814                             },
41815                             {
41816                                 'name': 'when_dt',
41817                                 'type': 'string'
41818                             },
41819                             {
41820                                 'name': 'end_dt',
41821                                 'type': 'string'
41822                             },
41823                             {
41824                                 'name': 'parent_id',
41825                                 'type': 'int'
41826                             },
41827                             {
41828                                 'name': 'product_id',
41829                                 'type': 'int'
41830                             },
41831                             {
41832                                 'name': 'productitem_id',
41833                                 'type': 'int'
41834                             },
41835                             {
41836                                 'name': 'guid',
41837                                 'type': 'int'
41838                             }
41839                         ]
41840                     }
41841                 },
41842                 toolbar : {
41843                     xtype: 'Toolbar',
41844                     xns: Roo,
41845                     items : [
41846                         {
41847                             xtype: 'Button',
41848                             xns: Roo.Toolbar,
41849                             listeners : {
41850                                 click : function (_self, e)
41851                                 {
41852                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41853                                     sd.setMonth(sd.getMonth()-1);
41854                                     _this.monthField.setValue(sd.format('Y-m-d'));
41855                                     _this.grid.ds.load({});
41856                                 }
41857                             },
41858                             text : "Back"
41859                         },
41860                         {
41861                             xtype: 'Separator',
41862                             xns: Roo.Toolbar
41863                         },
41864                         {
41865                             xtype: 'MonthField',
41866                             xns: Roo.form,
41867                             listeners : {
41868                                 render : function (_self)
41869                                 {
41870                                     _this.monthField = _self;
41871                                    // _this.monthField.set  today
41872                                 },
41873                                 select : function (combo, date)
41874                                 {
41875                                     _this.grid.ds.load({});
41876                                 }
41877                             },
41878                             value : (function() { return new Date(); })()
41879                         },
41880                         {
41881                             xtype: 'Separator',
41882                             xns: Roo.Toolbar
41883                         },
41884                         {
41885                             xtype: 'TextItem',
41886                             xns: Roo.Toolbar,
41887                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41888                         },
41889                         {
41890                             xtype: 'Fill',
41891                             xns: Roo.Toolbar
41892                         },
41893                         {
41894                             xtype: 'Button',
41895                             xns: Roo.Toolbar,
41896                             listeners : {
41897                                 click : function (_self, e)
41898                                 {
41899                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41900                                     sd.setMonth(sd.getMonth()+1);
41901                                     _this.monthField.setValue(sd.format('Y-m-d'));
41902                                     _this.grid.ds.load({});
41903                                 }
41904                             },
41905                             text : "Next"
41906                         }
41907                     ]
41908                 },
41909                  
41910             }
41911         };
41912         
41913         *//*
41914  * Based on:
41915  * Ext JS Library 1.1.1
41916  * Copyright(c) 2006-2007, Ext JS, LLC.
41917  *
41918  * Originally Released Under LGPL - original licence link has changed is not relivant.
41919  *
41920  * Fork - LGPL
41921  * <script type="text/javascript">
41922  */
41923  
41924 /**
41925  * @class Roo.LoadMask
41926  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41927  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41928  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41929  * element's UpdateManager load indicator and will be destroyed after the initial load.
41930  * @constructor
41931  * Create a new LoadMask
41932  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41933  * @param {Object} config The config object
41934  */
41935 Roo.LoadMask = function(el, config){
41936     this.el = Roo.get(el);
41937     Roo.apply(this, config);
41938     if(this.store){
41939         this.store.on('beforeload', this.onBeforeLoad, this);
41940         this.store.on('load', this.onLoad, this);
41941         this.store.on('loadexception', this.onLoadException, this);
41942         this.removeMask = false;
41943     }else{
41944         var um = this.el.getUpdateManager();
41945         um.showLoadIndicator = false; // disable the default indicator
41946         um.on('beforeupdate', this.onBeforeLoad, this);
41947         um.on('update', this.onLoad, this);
41948         um.on('failure', this.onLoad, this);
41949         this.removeMask = true;
41950     }
41951 };
41952
41953 Roo.LoadMask.prototype = {
41954     /**
41955      * @cfg {Boolean} removeMask
41956      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41957      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41958      */
41959     /**
41960      * @cfg {String} msg
41961      * The text to display in a centered loading message box (defaults to 'Loading...')
41962      */
41963     msg : 'Loading...',
41964     /**
41965      * @cfg {String} msgCls
41966      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41967      */
41968     msgCls : 'x-mask-loading',
41969
41970     /**
41971      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41972      * @type Boolean
41973      */
41974     disabled: false,
41975
41976     /**
41977      * Disables the mask to prevent it from being displayed
41978      */
41979     disable : function(){
41980        this.disabled = true;
41981     },
41982
41983     /**
41984      * Enables the mask so that it can be displayed
41985      */
41986     enable : function(){
41987         this.disabled = false;
41988     },
41989     
41990     onLoadException : function()
41991     {
41992         Roo.log(arguments);
41993         
41994         if (typeof(arguments[3]) != 'undefined') {
41995             Roo.MessageBox.alert("Error loading",arguments[3]);
41996         } 
41997         /*
41998         try {
41999             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42000                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42001             }   
42002         } catch(e) {
42003             
42004         }
42005         */
42006     
42007         
42008         
42009         this.el.unmask(this.removeMask);
42010     },
42011     // private
42012     onLoad : function()
42013     {
42014         this.el.unmask(this.removeMask);
42015     },
42016
42017     // private
42018     onBeforeLoad : function(){
42019         if(!this.disabled){
42020             this.el.mask(this.msg, this.msgCls);
42021         }
42022     },
42023
42024     // private
42025     destroy : function(){
42026         if(this.store){
42027             this.store.un('beforeload', this.onBeforeLoad, this);
42028             this.store.un('load', this.onLoad, this);
42029             this.store.un('loadexception', this.onLoadException, this);
42030         }else{
42031             var um = this.el.getUpdateManager();
42032             um.un('beforeupdate', this.onBeforeLoad, this);
42033             um.un('update', this.onLoad, this);
42034             um.un('failure', this.onLoad, this);
42035         }
42036     }
42037 };/*
42038  * Based on:
42039  * Ext JS Library 1.1.1
42040  * Copyright(c) 2006-2007, Ext JS, LLC.
42041  *
42042  * Originally Released Under LGPL - original licence link has changed is not relivant.
42043  *
42044  * Fork - LGPL
42045  * <script type="text/javascript">
42046  */
42047
42048
42049 /**
42050  * @class Roo.XTemplate
42051  * @extends Roo.Template
42052  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42053 <pre><code>
42054 var t = new Roo.XTemplate(
42055         '&lt;select name="{name}"&gt;',
42056                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42057         '&lt;/select&gt;'
42058 );
42059  
42060 // then append, applying the master template values
42061  </code></pre>
42062  *
42063  * Supported features:
42064  *
42065  *  Tags:
42066
42067 <pre><code>
42068       {a_variable} - output encoded.
42069       {a_variable.format:("Y-m-d")} - call a method on the variable
42070       {a_variable:raw} - unencoded output
42071       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42072       {a_variable:this.method_on_template(...)} - call a method on the template object.
42073  
42074 </code></pre>
42075  *  The tpl tag:
42076 <pre><code>
42077         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42078         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42079         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42080         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42081   
42082         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42083         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42084 </code></pre>
42085  *      
42086  */
42087 Roo.XTemplate = function()
42088 {
42089     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42090     if (this.html) {
42091         this.compile();
42092     }
42093 };
42094
42095
42096 Roo.extend(Roo.XTemplate, Roo.Template, {
42097
42098     /**
42099      * The various sub templates
42100      */
42101     tpls : false,
42102     /**
42103      *
42104      * basic tag replacing syntax
42105      * WORD:WORD()
42106      *
42107      * // you can fake an object call by doing this
42108      *  x.t:(test,tesT) 
42109      * 
42110      */
42111     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42112
42113     /**
42114      * compile the template
42115      *
42116      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42117      *
42118      */
42119     compile: function()
42120     {
42121         var s = this.html;
42122      
42123         s = ['<tpl>', s, '</tpl>'].join('');
42124     
42125         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42126             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42127             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42128             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42129             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42130             m,
42131             id     = 0,
42132             tpls   = [];
42133     
42134         while(true == !!(m = s.match(re))){
42135             var forMatch   = m[0].match(nameRe),
42136                 ifMatch   = m[0].match(ifRe),
42137                 execMatch   = m[0].match(execRe),
42138                 namedMatch   = m[0].match(namedRe),
42139                 
42140                 exp  = null, 
42141                 fn   = null,
42142                 exec = null,
42143                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42144                 
42145             if (ifMatch) {
42146                 // if - puts fn into test..
42147                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42148                 if(exp){
42149                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42150                 }
42151             }
42152             
42153             if (execMatch) {
42154                 // exec - calls a function... returns empty if true is  returned.
42155                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42156                 if(exp){
42157                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42158                 }
42159             }
42160             
42161             
42162             if (name) {
42163                 // for = 
42164                 switch(name){
42165                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42166                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42167                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42168                 }
42169             }
42170             var uid = namedMatch ? namedMatch[1] : id;
42171             
42172             
42173             tpls.push({
42174                 id:     namedMatch ? namedMatch[1] : id,
42175                 target: name,
42176                 exec:   exec,
42177                 test:   fn,
42178                 body:   m[1] || ''
42179             });
42180             if (namedMatch) {
42181                 s = s.replace(m[0], '');
42182             } else { 
42183                 s = s.replace(m[0], '{xtpl'+ id + '}');
42184             }
42185             ++id;
42186         }
42187         this.tpls = [];
42188         for(var i = tpls.length-1; i >= 0; --i){
42189             this.compileTpl(tpls[i]);
42190             this.tpls[tpls[i].id] = tpls[i];
42191         }
42192         this.master = tpls[tpls.length-1];
42193         return this;
42194     },
42195     /**
42196      * same as applyTemplate, except it's done to one of the subTemplates
42197      * when using named templates, you can do:
42198      *
42199      * var str = pl.applySubTemplate('your-name', values);
42200      *
42201      * 
42202      * @param {Number} id of the template
42203      * @param {Object} values to apply to template
42204      * @param {Object} parent (normaly the instance of this object)
42205      */
42206     applySubTemplate : function(id, values, parent)
42207     {
42208         
42209         
42210         var t = this.tpls[id];
42211         
42212         
42213         try { 
42214             if(t.test && !t.test.call(this, values, parent)){
42215                 return '';
42216             }
42217         } catch(e) {
42218             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42219             Roo.log(e.toString());
42220             Roo.log(t.test);
42221             return ''
42222         }
42223         try { 
42224             
42225             if(t.exec && t.exec.call(this, values, parent)){
42226                 return '';
42227             }
42228         } catch(e) {
42229             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42230             Roo.log(e.toString());
42231             Roo.log(t.exec);
42232             return ''
42233         }
42234         try {
42235             var vs = t.target ? t.target.call(this, values, parent) : values;
42236             parent = t.target ? values : parent;
42237             if(t.target && vs instanceof Array){
42238                 var buf = [];
42239                 for(var i = 0, len = vs.length; i < len; i++){
42240                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42241                 }
42242                 return buf.join('');
42243             }
42244             return t.compiled.call(this, vs, parent);
42245         } catch (e) {
42246             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42247             Roo.log(e.toString());
42248             Roo.log(t.compiled);
42249             return '';
42250         }
42251     },
42252
42253     compileTpl : function(tpl)
42254     {
42255         var fm = Roo.util.Format;
42256         var useF = this.disableFormats !== true;
42257         var sep = Roo.isGecko ? "+" : ",";
42258         var undef = function(str) {
42259             Roo.log("Property not found :"  + str);
42260             return '';
42261         };
42262         
42263         var fn = function(m, name, format, args)
42264         {
42265             //Roo.log(arguments);
42266             args = args ? args.replace(/\\'/g,"'") : args;
42267             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42268             if (typeof(format) == 'undefined') {
42269                 format= 'htmlEncode';
42270             }
42271             if (format == 'raw' ) {
42272                 format = false;
42273             }
42274             
42275             if(name.substr(0, 4) == 'xtpl'){
42276                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42277             }
42278             
42279             // build an array of options to determine if value is undefined..
42280             
42281             // basically get 'xxxx.yyyy' then do
42282             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42283             //    (function () { Roo.log("Property not found"); return ''; })() :
42284             //    ......
42285             
42286             var udef_ar = [];
42287             var lookfor = '';
42288             Roo.each(name.split('.'), function(st) {
42289                 lookfor += (lookfor.length ? '.': '') + st;
42290                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42291             });
42292             
42293             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42294             
42295             
42296             if(format && useF){
42297                 
42298                 args = args ? ',' + args : "";
42299                  
42300                 if(format.substr(0, 5) != "this."){
42301                     format = "fm." + format + '(';
42302                 }else{
42303                     format = 'this.call("'+ format.substr(5) + '", ';
42304                     args = ", values";
42305                 }
42306                 
42307                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42308             }
42309              
42310             if (args.length) {
42311                 // called with xxyx.yuu:(test,test)
42312                 // change to ()
42313                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42314             }
42315             // raw.. - :raw modifier..
42316             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42317             
42318         };
42319         var body;
42320         // branched to use + in gecko and [].join() in others
42321         if(Roo.isGecko){
42322             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42323                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42324                     "';};};";
42325         }else{
42326             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42327             body.push(tpl.body.replace(/(\r\n|\n)/g,
42328                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42329             body.push("'].join('');};};");
42330             body = body.join('');
42331         }
42332         
42333         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42334        
42335         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42336         eval(body);
42337         
42338         return this;
42339     },
42340
42341     applyTemplate : function(values){
42342         return this.master.compiled.call(this, values, {});
42343         //var s = this.subs;
42344     },
42345
42346     apply : function(){
42347         return this.applyTemplate.apply(this, arguments);
42348     }
42349
42350  });
42351
42352 Roo.XTemplate.from = function(el){
42353     el = Roo.getDom(el);
42354     return new Roo.XTemplate(el.value || el.innerHTML);
42355 };