roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) {
4462             val = 0;
4463         }
4464         return val;
4465     },
4466     
4467     /**
4468      * Integer sorting
4469      * @param {Mixed} s The value being converted
4470      * @return {Number} The comparison value
4471      */
4472     asInt : function(s) {
4473         var val = parseInt(String(s).replace(/,/g, ""));
4474         if(isNaN(val)) {
4475             val = 0;
4476         }
4477         return val;
4478     }
4479 };/*
4480  * Based on:
4481  * Ext JS Library 1.1.1
4482  * Copyright(c) 2006-2007, Ext JS, LLC.
4483  *
4484  * Originally Released Under LGPL - original licence link has changed is not relivant.
4485  *
4486  * Fork - LGPL
4487  * <script type="text/javascript">
4488  */
4489
4490 /**
4491 * @class Roo.data.Record
4492  * Instances of this class encapsulate both record <em>definition</em> information, and record
4493  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4494  * to access Records cached in an {@link Roo.data.Store} object.<br>
4495  * <p>
4496  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4497  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4498  * objects.<br>
4499  * <p>
4500  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4501  * @constructor
4502  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4503  * {@link #create}. The parameters are the same.
4504  * @param {Array} data An associative Array of data values keyed by the field name.
4505  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4506  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4507  * not specified an integer id is generated.
4508  */
4509 Roo.data.Record = function(data, id){
4510     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4511     this.data = data;
4512 };
4513
4514 /**
4515  * Generate a constructor for a specific record layout.
4516  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4517  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4518  * Each field definition object may contain the following properties: <ul>
4519  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4520  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4521  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4522  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4523  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4524  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4525  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4526  * this may be omitted.</p></li>
4527  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4528  * <ul><li>auto (Default, implies no conversion)</li>
4529  * <li>string</li>
4530  * <li>int</li>
4531  * <li>float</li>
4532  * <li>boolean</li>
4533  * <li>date</li></ul></p></li>
4534  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4535  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4536  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4537  * by the Reader into an object that will be stored in the Record. It is passed the
4538  * following parameters:<ul>
4539  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4540  * </ul></p></li>
4541  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4542  * </ul>
4543  * <br>usage:<br><pre><code>
4544 var TopicRecord = Roo.data.Record.create(
4545     {name: 'title', mapping: 'topic_title'},
4546     {name: 'author', mapping: 'username'},
4547     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4548     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4549     {name: 'lastPoster', mapping: 'user2'},
4550     {name: 'excerpt', mapping: 'post_text'}
4551 );
4552
4553 var myNewRecord = new TopicRecord({
4554     title: 'Do my job please',
4555     author: 'noobie',
4556     totalPosts: 1,
4557     lastPost: new Date(),
4558     lastPoster: 'Animal',
4559     excerpt: 'No way dude!'
4560 });
4561 myStore.add(myNewRecord);
4562 </code></pre>
4563  * @method create
4564  * @static
4565  */
4566 Roo.data.Record.create = function(o){
4567     var f = function(){
4568         f.superclass.constructor.apply(this, arguments);
4569     };
4570     Roo.extend(f, Roo.data.Record);
4571     var p = f.prototype;
4572     p.fields = new Roo.util.MixedCollection(false, function(field){
4573         return field.name;
4574     });
4575     for(var i = 0, len = o.length; i < len; i++){
4576         p.fields.add(new Roo.data.Field(o[i]));
4577     }
4578     f.getField = function(name){
4579         return p.fields.get(name);  
4580     };
4581     return f;
4582 };
4583
4584 Roo.data.Record.AUTO_ID = 1000;
4585 Roo.data.Record.EDIT = 'edit';
4586 Roo.data.Record.REJECT = 'reject';
4587 Roo.data.Record.COMMIT = 'commit';
4588
4589 Roo.data.Record.prototype = {
4590     /**
4591      * Readonly flag - true if this record has been modified.
4592      * @type Boolean
4593      */
4594     dirty : false,
4595     editing : false,
4596     error: null,
4597     modified: null,
4598
4599     // private
4600     join : function(store){
4601         this.store = store;
4602     },
4603
4604     /**
4605      * Set the named field to the specified value.
4606      * @param {String} name The name of the field to set.
4607      * @param {Object} value The value to set the field to.
4608      */
4609     set : function(name, value){
4610         if(this.data[name] == value){
4611             return;
4612         }
4613         this.dirty = true;
4614         if(!this.modified){
4615             this.modified = {};
4616         }
4617         if(typeof this.modified[name] == 'undefined'){
4618             this.modified[name] = this.data[name];
4619         }
4620         this.data[name] = value;
4621         if(!this.editing && this.store){
4622             this.store.afterEdit(this);
4623         }       
4624     },
4625
4626     /**
4627      * Get the value of the named field.
4628      * @param {String} name The name of the field to get the value of.
4629      * @return {Object} The value of the field.
4630      */
4631     get : function(name){
4632         return this.data[name]; 
4633     },
4634
4635     // private
4636     beginEdit : function(){
4637         this.editing = true;
4638         this.modified = {}; 
4639     },
4640
4641     // private
4642     cancelEdit : function(){
4643         this.editing = false;
4644         delete this.modified;
4645     },
4646
4647     // private
4648     endEdit : function(){
4649         this.editing = false;
4650         if(this.dirty && this.store){
4651             this.store.afterEdit(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Rejects all changes made to the Record since either creation, or the last commit operation.
4658      * Modified fields are reverted to their original values.
4659      * <p>
4660      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4661      * of reject operations.
4662      */
4663     reject : function(){
4664         var m = this.modified;
4665         for(var n in m){
4666             if(typeof m[n] != "function"){
4667                 this.data[n] = m[n];
4668             }
4669         }
4670         this.dirty = false;
4671         delete this.modified;
4672         this.editing = false;
4673         if(this.store){
4674             this.store.afterReject(this);
4675         }
4676     },
4677
4678     /**
4679      * Usually called by the {@link Roo.data.Store} which owns the Record.
4680      * Commits all changes made to the Record since either creation, or the last commit operation.
4681      * <p>
4682      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4683      * of commit operations.
4684      */
4685     commit : function(){
4686         this.dirty = false;
4687         delete this.modified;
4688         this.editing = false;
4689         if(this.store){
4690             this.store.afterCommit(this);
4691         }
4692     },
4693
4694     // private
4695     hasError : function(){
4696         return this.error != null;
4697     },
4698
4699     // private
4700     clearError : function(){
4701         this.error = null;
4702     },
4703
4704     /**
4705      * Creates a copy of this record.
4706      * @param {String} id (optional) A new record id if you don't want to use this record's id
4707      * @return {Record}
4708      */
4709     copy : function(newId) {
4710         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4711     }
4712 };/*
4713  * Based on:
4714  * Ext JS Library 1.1.1
4715  * Copyright(c) 2006-2007, Ext JS, LLC.
4716  *
4717  * Originally Released Under LGPL - original licence link has changed is not relivant.
4718  *
4719  * Fork - LGPL
4720  * <script type="text/javascript">
4721  */
4722
4723
4724
4725 /**
4726  * @class Roo.data.Store
4727  * @extends Roo.util.Observable
4728  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4729  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4730  * <p>
4731  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4732  * has no knowledge of the format of the data returned by the Proxy.<br>
4733  * <p>
4734  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4735  * instances from the data object. These records are cached and made available through accessor functions.
4736  * @constructor
4737  * Creates a new Store.
4738  * @param {Object} config A config object containing the objects needed for the Store to access data,
4739  * and read the data into Records.
4740  */
4741 Roo.data.Store = function(config){
4742     this.data = new Roo.util.MixedCollection(false);
4743     this.data.getKey = function(o){
4744         return o.id;
4745     };
4746     this.baseParams = {};
4747     // private
4748     this.paramNames = {
4749         "start" : "start",
4750         "limit" : "limit",
4751         "sort" : "sort",
4752         "dir" : "dir",
4753         "multisort" : "_multisort"
4754     };
4755
4756     if(config && config.data){
4757         this.inlineData = config.data;
4758         delete config.data;
4759     }
4760
4761     Roo.apply(this, config);
4762     
4763     if(this.reader){ // reader passed
4764         this.reader = Roo.factory(this.reader, Roo.data);
4765         this.reader.xmodule = this.xmodule || false;
4766         if(!this.recordType){
4767             this.recordType = this.reader.recordType;
4768         }
4769         if(this.reader.onMetaChange){
4770             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4771         }
4772     }
4773
4774     if(this.recordType){
4775         this.fields = this.recordType.prototype.fields;
4776     }
4777     this.modified = [];
4778
4779     this.addEvents({
4780         /**
4781          * @event datachanged
4782          * Fires when the data cache has changed, and a widget which is using this Store
4783          * as a Record cache should refresh its view.
4784          * @param {Store} this
4785          */
4786         datachanged : true,
4787         /**
4788          * @event metachange
4789          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4790          * @param {Store} this
4791          * @param {Object} meta The JSON metadata
4792          */
4793         metachange : true,
4794         /**
4795          * @event add
4796          * Fires when Records have been added to the Store
4797          * @param {Store} this
4798          * @param {Roo.data.Record[]} records The array of Records added
4799          * @param {Number} index The index at which the record(s) were added
4800          */
4801         add : true,
4802         /**
4803          * @event remove
4804          * Fires when a Record has been removed from the Store
4805          * @param {Store} this
4806          * @param {Roo.data.Record} record The Record that was removed
4807          * @param {Number} index The index at which the record was removed
4808          */
4809         remove : true,
4810         /**
4811          * @event update
4812          * Fires when a Record has been updated
4813          * @param {Store} this
4814          * @param {Roo.data.Record} record The Record that was updated
4815          * @param {String} operation The update operation being performed.  Value may be one of:
4816          * <pre><code>
4817  Roo.data.Record.EDIT
4818  Roo.data.Record.REJECT
4819  Roo.data.Record.COMMIT
4820          * </code></pre>
4821          */
4822         update : true,
4823         /**
4824          * @event clear
4825          * Fires when the data cache has been cleared.
4826          * @param {Store} this
4827          */
4828         clear : true,
4829         /**
4830          * @event beforeload
4831          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4832          * the load action will be canceled.
4833          * @param {Store} this
4834          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4835          */
4836         beforeload : true,
4837         /**
4838          * @event beforeloadadd
4839          * Fires after a new set of Records has been loaded.
4840          * @param {Store} this
4841          * @param {Roo.data.Record[]} records The Records that were loaded
4842          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4843          */
4844         beforeloadadd : true,
4845         /**
4846          * @event load
4847          * Fires after a new set of Records has been loaded, before they are added to the store.
4848          * @param {Store} this
4849          * @param {Roo.data.Record[]} records The Records that were loaded
4850          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4851          * @params {Object} return from reader
4852          */
4853         load : true,
4854         /**
4855          * @event loadexception
4856          * Fires if an exception occurs in the Proxy during loading.
4857          * Called with the signature of the Proxy's "loadexception" event.
4858          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4859          * 
4860          * @param {Proxy} 
4861          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4862          * @param {Object} load options 
4863          * @param {Object} jsonData from your request (normally this contains the Exception)
4864          */
4865         loadexception : true
4866     });
4867     
4868     if(this.proxy){
4869         this.proxy = Roo.factory(this.proxy, Roo.data);
4870         this.proxy.xmodule = this.xmodule || false;
4871         this.relayEvents(this.proxy,  ["loadexception"]);
4872     }
4873     this.sortToggle = {};
4874     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4875
4876     Roo.data.Store.superclass.constructor.call(this);
4877
4878     if(this.inlineData){
4879         this.loadData(this.inlineData);
4880         delete this.inlineData;
4881     }
4882 };
4883
4884 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4885      /**
4886     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4887     * without a remote query - used by combo/forms at present.
4888     */
4889     
4890     /**
4891     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4892     */
4893     /**
4894     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4895     */
4896     /**
4897     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4898     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4899     */
4900     /**
4901     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4902     * on any HTTP request
4903     */
4904     /**
4905     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4906     */
4907     /**
4908     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4909     */
4910     multiSort: false,
4911     /**
4912     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4913     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4914     */
4915     remoteSort : false,
4916
4917     /**
4918     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4919      * loaded or when a record is removed. (defaults to false).
4920     */
4921     pruneModifiedRecords : false,
4922
4923     // private
4924     lastOptions : null,
4925
4926     /**
4927      * Add Records to the Store and fires the add event.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     add : function(records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             records[i].join(this);
4934         }
4935         var index = this.data.length;
4936         this.data.addAll(records);
4937         this.fireEvent("add", this, records, index);
4938     },
4939
4940     /**
4941      * Remove a Record from the Store and fires the remove event.
4942      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4943      */
4944     remove : function(record){
4945         var index = this.data.indexOf(record);
4946         this.data.removeAt(index);
4947         if(this.pruneModifiedRecords){
4948             this.modified.remove(record);
4949         }
4950         this.fireEvent("remove", this, record, index);
4951     },
4952
4953     /**
4954      * Remove all Records from the Store and fires the clear event.
4955      */
4956     removeAll : function(){
4957         this.data.clear();
4958         if(this.pruneModifiedRecords){
4959             this.modified = [];
4960         }
4961         this.fireEvent("clear", this);
4962     },
4963
4964     /**
4965      * Inserts Records to the Store at the given index and fires the add event.
4966      * @param {Number} index The start index at which to insert the passed Records.
4967      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4968      */
4969     insert : function(index, records){
4970         records = [].concat(records);
4971         for(var i = 0, len = records.length; i < len; i++){
4972             this.data.insert(index, records[i]);
4973             records[i].join(this);
4974         }
4975         this.fireEvent("add", this, records, index);
4976     },
4977
4978     /**
4979      * Get the index within the cache of the passed Record.
4980      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4981      * @return {Number} The index of the passed Record. Returns -1 if not found.
4982      */
4983     indexOf : function(record){
4984         return this.data.indexOf(record);
4985     },
4986
4987     /**
4988      * Get the index within the cache of the Record with the passed id.
4989      * @param {String} id The id of the Record to find.
4990      * @return {Number} The index of the Record. Returns -1 if not found.
4991      */
4992     indexOfId : function(id){
4993         return this.data.indexOfKey(id);
4994     },
4995
4996     /**
4997      * Get the Record with the specified id.
4998      * @param {String} id The id of the Record to find.
4999      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
5000      */
5001     getById : function(id){
5002         return this.data.key(id);
5003     },
5004
5005     /**
5006      * Get the Record at the specified index.
5007      * @param {Number} index The index of the Record to find.
5008      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5009      */
5010     getAt : function(index){
5011         return this.data.itemAt(index);
5012     },
5013
5014     /**
5015      * Returns a range of Records between specified indices.
5016      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5017      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5018      * @return {Roo.data.Record[]} An array of Records
5019      */
5020     getRange : function(start, end){
5021         return this.data.getRange(start, end);
5022     },
5023
5024     // private
5025     storeOptions : function(o){
5026         o = Roo.apply({}, o);
5027         delete o.callback;
5028         delete o.scope;
5029         this.lastOptions = o;
5030     },
5031
5032     /**
5033      * Loads the Record cache from the configured Proxy using the configured Reader.
5034      * <p>
5035      * If using remote paging, then the first load call must specify the <em>start</em>
5036      * and <em>limit</em> properties in the options.params property to establish the initial
5037      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5038      * <p>
5039      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5040      * and this call will return before the new data has been loaded. Perform any post-processing
5041      * in a callback function, or in a "load" event handler.</strong>
5042      * <p>
5043      * @param {Object} options An object containing properties which control loading options:<ul>
5044      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5045      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5046      * passed the following arguments:<ul>
5047      * <li>r : Roo.data.Record[]</li>
5048      * <li>options: Options object from the load call</li>
5049      * <li>success: Boolean success indicator</li></ul></li>
5050      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5051      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5052      * </ul>
5053      */
5054     load : function(options){
5055         options = options || {};
5056         if(this.fireEvent("beforeload", this, options) !== false){
5057             this.storeOptions(options);
5058             var p = Roo.apply(options.params || {}, this.baseParams);
5059             // if meta was not loaded from remote source.. try requesting it.
5060             if (!this.reader.metaFromRemote) {
5061                 p._requestMeta = 1;
5062             }
5063             if(this.sortInfo && this.remoteSort){
5064                 var pn = this.paramNames;
5065                 p[pn["sort"]] = this.sortInfo.field;
5066                 p[pn["dir"]] = this.sortInfo.direction;
5067             }
5068             if (this.multiSort) {
5069                 var pn = this.paramNames;
5070                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5071             }
5072             
5073             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5074         }
5075     },
5076
5077     /**
5078      * Reloads the Record cache from the configured Proxy using the configured Reader and
5079      * the options from the last load operation performed.
5080      * @param {Object} options (optional) An object containing properties which may override the options
5081      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5082      * the most recently used options are reused).
5083      */
5084     reload : function(options){
5085         this.load(Roo.applyIf(options||{}, this.lastOptions));
5086     },
5087
5088     // private
5089     // Called as a callback by the Reader during a load operation.
5090     loadRecords : function(o, options, success){
5091         if(!o || success === false){
5092             if(success !== false){
5093                 this.fireEvent("load", this, [], options, o);
5094             }
5095             if(options.callback){
5096                 options.callback.call(options.scope || this, [], options, false);
5097             }
5098             return;
5099         }
5100         // if data returned failure - throw an exception.
5101         if (o.success === false) {
5102             // show a message if no listener is registered.
5103             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5104                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5105             }
5106             // loadmask wil be hooked into this..
5107             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5108             return;
5109         }
5110         var r = o.records, t = o.totalRecords || r.length;
5111         
5112         this.fireEvent("beforeloadadd", this, r, options, o);
5113         
5114         if(!options || options.add !== true){
5115             if(this.pruneModifiedRecords){
5116                 this.modified = [];
5117             }
5118             for(var i = 0, len = r.length; i < len; i++){
5119                 r[i].join(this);
5120             }
5121             if(this.snapshot){
5122                 this.data = this.snapshot;
5123                 delete this.snapshot;
5124             }
5125             this.data.clear();
5126             this.data.addAll(r);
5127             this.totalLength = t;
5128             this.applySort();
5129             this.fireEvent("datachanged", this);
5130         }else{
5131             this.totalLength = Math.max(t, this.data.length+r.length);
5132             this.add(r);
5133         }
5134         this.fireEvent("load", this, r, options, o);
5135         if(options.callback){
5136             options.callback.call(options.scope || this, r, options, true);
5137         }
5138     },
5139
5140
5141     /**
5142      * Loads data from a passed data block. A Reader which understands the format of the data
5143      * must have been configured in the constructor.
5144      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5145      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5146      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5147      */
5148     loadData : function(o, append){
5149         var r = this.reader.readRecords(o);
5150         this.loadRecords(r, {add: append}, true);
5151     },
5152
5153     /**
5154      * Gets the number of cached records.
5155      * <p>
5156      * <em>If using paging, this may not be the total size of the dataset. If the data object
5157      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5158      * the data set size</em>
5159      */
5160     getCount : function(){
5161         return this.data.length || 0;
5162     },
5163
5164     /**
5165      * Gets the total number of records in the dataset as returned by the server.
5166      * <p>
5167      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5168      * the dataset size</em>
5169      */
5170     getTotalCount : function(){
5171         return this.totalLength || 0;
5172     },
5173
5174     /**
5175      * Returns the sort state of the Store as an object with two properties:
5176      * <pre><code>
5177  field {String} The name of the field by which the Records are sorted
5178  direction {String} The sort order, "ASC" or "DESC"
5179      * </code></pre>
5180      */
5181     getSortState : function(){
5182         return this.sortInfo;
5183     },
5184
5185     // private
5186     applySort : function(){
5187         if(this.sortInfo && !this.remoteSort){
5188             var s = this.sortInfo, f = s.field;
5189             var st = this.fields.get(f).sortType;
5190             var fn = function(r1, r2){
5191                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5192                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5193             };
5194             this.data.sort(s.direction, fn);
5195             if(this.snapshot && this.snapshot != this.data){
5196                 this.snapshot.sort(s.direction, fn);
5197             }
5198         }
5199     },
5200
5201     /**
5202      * Sets the default sort column and order to be used by the next load operation.
5203      * @param {String} fieldName The name of the field to sort by.
5204      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5205      */
5206     setDefaultSort : function(field, dir){
5207         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5208     },
5209
5210     /**
5211      * Sort the Records.
5212      * If remote sorting is used, the sort is performed on the server, and the cache is
5213      * reloaded. If local sorting is used, the cache is sorted internally.
5214      * @param {String} fieldName The name of the field to sort by.
5215      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5216      */
5217     sort : function(fieldName, dir){
5218         var f = this.fields.get(fieldName);
5219         if(!dir){
5220             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5221             
5222             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5223                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5224             }else{
5225                 dir = f.sortDir;
5226             }
5227         }
5228         this.sortToggle[f.name] = dir;
5229         this.sortInfo = {field: f.name, direction: dir};
5230         if(!this.remoteSort){
5231             this.applySort();
5232             this.fireEvent("datachanged", this);
5233         }else{
5234             this.load(this.lastOptions);
5235         }
5236     },
5237
5238     /**
5239      * Calls the specified function for each of the Records in the cache.
5240      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5241      * Returning <em>false</em> aborts and exits the iteration.
5242      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5243      */
5244     each : function(fn, scope){
5245         this.data.each(fn, scope);
5246     },
5247
5248     /**
5249      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5250      * (e.g., during paging).
5251      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5252      */
5253     getModifiedRecords : function(){
5254         return this.modified;
5255     },
5256
5257     // private
5258     createFilterFn : function(property, value, anyMatch){
5259         if(!value.exec){ // not a regex
5260             value = String(value);
5261             if(value.length == 0){
5262                 return false;
5263             }
5264             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5265         }
5266         return function(r){
5267             return value.test(r.data[property]);
5268         };
5269     },
5270
5271     /**
5272      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5273      * @param {String} property A field on your records
5274      * @param {Number} start The record index to start at (defaults to 0)
5275      * @param {Number} end The last record index to include (defaults to length - 1)
5276      * @return {Number} The sum
5277      */
5278     sum : function(property, start, end){
5279         var rs = this.data.items, v = 0;
5280         start = start || 0;
5281         end = (end || end === 0) ? end : rs.length-1;
5282
5283         for(var i = start; i <= end; i++){
5284             v += (rs[i].data[property] || 0);
5285         }
5286         return v;
5287     },
5288
5289     /**
5290      * Filter the records by a specified property.
5291      * @param {String} field A field on your records
5292      * @param {String/RegExp} value Either a string that the field
5293      * should start with or a RegExp to test against the field
5294      * @param {Boolean} anyMatch True to match any part not just the beginning
5295      */
5296     filter : function(property, value, anyMatch){
5297         var fn = this.createFilterFn(property, value, anyMatch);
5298         return fn ? this.filterBy(fn) : this.clearFilter();
5299     },
5300
5301     /**
5302      * Filter by a function. The specified function will be called with each
5303      * record in this data source. If the function returns true the record is included,
5304      * otherwise it is filtered.
5305      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5306      * @param {Object} scope (optional) The scope of the function (defaults to this)
5307      */
5308     filterBy : function(fn, scope){
5309         this.snapshot = this.snapshot || this.data;
5310         this.data = this.queryBy(fn, scope||this);
5311         this.fireEvent("datachanged", this);
5312     },
5313
5314     /**
5315      * Query the records by a specified property.
5316      * @param {String} field A field on your records
5317      * @param {String/RegExp} value Either a string that the field
5318      * should start with or a RegExp to test against the field
5319      * @param {Boolean} anyMatch True to match any part not just the beginning
5320      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5321      */
5322     query : function(property, value, anyMatch){
5323         var fn = this.createFilterFn(property, value, anyMatch);
5324         return fn ? this.queryBy(fn) : this.data.clone();
5325     },
5326
5327     /**
5328      * Query by a function. The specified function will be called with each
5329      * record in this data source. If the function returns true the record is included
5330      * in the results.
5331      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5332      * @param {Object} scope (optional) The scope of the function (defaults to this)
5333       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5334      **/
5335     queryBy : function(fn, scope){
5336         var data = this.snapshot || this.data;
5337         return data.filterBy(fn, scope||this);
5338     },
5339
5340     /**
5341      * Collects unique values for a particular dataIndex from this store.
5342      * @param {String} dataIndex The property to collect
5343      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5344      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5345      * @return {Array} An array of the unique values
5346      **/
5347     collect : function(dataIndex, allowNull, bypassFilter){
5348         var d = (bypassFilter === true && this.snapshot) ?
5349                 this.snapshot.items : this.data.items;
5350         var v, sv, r = [], l = {};
5351         for(var i = 0, len = d.length; i < len; i++){
5352             v = d[i].data[dataIndex];
5353             sv = String(v);
5354             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5355                 l[sv] = true;
5356                 r[r.length] = v;
5357             }
5358         }
5359         return r;
5360     },
5361
5362     /**
5363      * Revert to a view of the Record cache with no filtering applied.
5364      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5365      */
5366     clearFilter : function(suppressEvent){
5367         if(this.snapshot && this.snapshot != this.data){
5368             this.data = this.snapshot;
5369             delete this.snapshot;
5370             if(suppressEvent !== true){
5371                 this.fireEvent("datachanged", this);
5372             }
5373         }
5374     },
5375
5376     // private
5377     afterEdit : function(record){
5378         if(this.modified.indexOf(record) == -1){
5379             this.modified.push(record);
5380         }
5381         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5382     },
5383     
5384     // private
5385     afterReject : function(record){
5386         this.modified.remove(record);
5387         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5388     },
5389
5390     // private
5391     afterCommit : function(record){
5392         this.modified.remove(record);
5393         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5394     },
5395
5396     /**
5397      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5398      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5399      */
5400     commitChanges : function(){
5401         var m = this.modified.slice(0);
5402         this.modified = [];
5403         for(var i = 0, len = m.length; i < len; i++){
5404             m[i].commit();
5405         }
5406     },
5407
5408     /**
5409      * Cancel outstanding changes on all changed records.
5410      */
5411     rejectChanges : function(){
5412         var m = this.modified.slice(0);
5413         this.modified = [];
5414         for(var i = 0, len = m.length; i < len; i++){
5415             m[i].reject();
5416         }
5417     },
5418
5419     onMetaChange : function(meta, rtype, o){
5420         this.recordType = rtype;
5421         this.fields = rtype.prototype.fields;
5422         delete this.snapshot;
5423         this.sortInfo = meta.sortInfo || this.sortInfo;
5424         this.modified = [];
5425         this.fireEvent('metachange', this, this.reader.meta);
5426     },
5427     
5428     moveIndex : function(data, type)
5429     {
5430         var index = this.indexOf(data);
5431         
5432         var newIndex = index + type;
5433         
5434         this.remove(data);
5435         
5436         this.insert(newIndex, data);
5437         
5438     }
5439 });/*
5440  * Based on:
5441  * Ext JS Library 1.1.1
5442  * Copyright(c) 2006-2007, Ext JS, LLC.
5443  *
5444  * Originally Released Under LGPL - original licence link has changed is not relivant.
5445  *
5446  * Fork - LGPL
5447  * <script type="text/javascript">
5448  */
5449
5450 /**
5451  * @class Roo.data.SimpleStore
5452  * @extends Roo.data.Store
5453  * Small helper class to make creating Stores from Array data easier.
5454  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5455  * @cfg {Array} fields An array of field definition objects, or field name strings.
5456  * @cfg {Array} data The multi-dimensional array of data
5457  * @constructor
5458  * @param {Object} config
5459  */
5460 Roo.data.SimpleStore = function(config){
5461     Roo.data.SimpleStore.superclass.constructor.call(this, {
5462         isLocal : true,
5463         reader: new Roo.data.ArrayReader({
5464                 id: config.id
5465             },
5466             Roo.data.Record.create(config.fields)
5467         ),
5468         proxy : new Roo.data.MemoryProxy(config.data)
5469     });
5470     this.load();
5471 };
5472 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5473  * Based on:
5474  * Ext JS Library 1.1.1
5475  * Copyright(c) 2006-2007, Ext JS, LLC.
5476  *
5477  * Originally Released Under LGPL - original licence link has changed is not relivant.
5478  *
5479  * Fork - LGPL
5480  * <script type="text/javascript">
5481  */
5482
5483 /**
5484 /**
5485  * @extends Roo.data.Store
5486  * @class Roo.data.JsonStore
5487  * Small helper class to make creating Stores for JSON data easier. <br/>
5488 <pre><code>
5489 var store = new Roo.data.JsonStore({
5490     url: 'get-images.php',
5491     root: 'images',
5492     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5493 });
5494 </code></pre>
5495  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5496  * JsonReader and HttpProxy (unless inline data is provided).</b>
5497  * @cfg {Array} fields An array of field definition objects, or field name strings.
5498  * @constructor
5499  * @param {Object} config
5500  */
5501 Roo.data.JsonStore = function(c){
5502     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5503         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5504         reader: new Roo.data.JsonReader(c, c.fields)
5505     }));
5506 };
5507 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5508  * Based on:
5509  * Ext JS Library 1.1.1
5510  * Copyright(c) 2006-2007, Ext JS, LLC.
5511  *
5512  * Originally Released Under LGPL - original licence link has changed is not relivant.
5513  *
5514  * Fork - LGPL
5515  * <script type="text/javascript">
5516  */
5517
5518  
5519 Roo.data.Field = function(config){
5520     if(typeof config == "string"){
5521         config = {name: config};
5522     }
5523     Roo.apply(this, config);
5524     
5525     if(!this.type){
5526         this.type = "auto";
5527     }
5528     
5529     var st = Roo.data.SortTypes;
5530     // named sortTypes are supported, here we look them up
5531     if(typeof this.sortType == "string"){
5532         this.sortType = st[this.sortType];
5533     }
5534     
5535     // set default sortType for strings and dates
5536     if(!this.sortType){
5537         switch(this.type){
5538             case "string":
5539                 this.sortType = st.asUCString;
5540                 break;
5541             case "date":
5542                 this.sortType = st.asDate;
5543                 break;
5544             default:
5545                 this.sortType = st.none;
5546         }
5547     }
5548
5549     // define once
5550     var stripRe = /[\$,%]/g;
5551
5552     // prebuilt conversion function for this field, instead of
5553     // switching every time we're reading a value
5554     if(!this.convert){
5555         var cv, dateFormat = this.dateFormat;
5556         switch(this.type){
5557             case "":
5558             case "auto":
5559             case undefined:
5560                 cv = function(v){ return v; };
5561                 break;
5562             case "string":
5563                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5564                 break;
5565             case "int":
5566                 cv = function(v){
5567                     return v !== undefined && v !== null && v !== '' ?
5568                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5569                     };
5570                 break;
5571             case "float":
5572                 cv = function(v){
5573                     return v !== undefined && v !== null && v !== '' ?
5574                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5575                     };
5576                 break;
5577             case "bool":
5578             case "boolean":
5579                 cv = function(v){ return v === true || v === "true" || v == 1; };
5580                 break;
5581             case "date":
5582                 cv = function(v){
5583                     if(!v){
5584                         return '';
5585                     }
5586                     if(v instanceof Date){
5587                         return v;
5588                     }
5589                     if(dateFormat){
5590                         if(dateFormat == "timestamp"){
5591                             return new Date(v*1000);
5592                         }
5593                         return Date.parseDate(v, dateFormat);
5594                     }
5595                     var parsed = Date.parse(v);
5596                     return parsed ? new Date(parsed) : null;
5597                 };
5598              break;
5599             
5600         }
5601         this.convert = cv;
5602     }
5603 };
5604
5605 Roo.data.Field.prototype = {
5606     dateFormat: null,
5607     defaultValue: "",
5608     mapping: null,
5609     sortType : null,
5610     sortDir : "ASC"
5611 };/*
5612  * Based on:
5613  * Ext JS Library 1.1.1
5614  * Copyright(c) 2006-2007, Ext JS, LLC.
5615  *
5616  * Originally Released Under LGPL - original licence link has changed is not relivant.
5617  *
5618  * Fork - LGPL
5619  * <script type="text/javascript">
5620  */
5621  
5622 // Base class for reading structured data from a data source.  This class is intended to be
5623 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5624
5625 /**
5626  * @class Roo.data.DataReader
5627  * Base class for reading structured data from a data source.  This class is intended to be
5628  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5629  */
5630
5631 Roo.data.DataReader = function(meta, recordType){
5632     
5633     this.meta = meta;
5634     
5635     this.recordType = recordType instanceof Array ? 
5636         Roo.data.Record.create(recordType) : recordType;
5637 };
5638
5639 Roo.data.DataReader.prototype = {
5640      /**
5641      * Create an empty record
5642      * @param {Object} data (optional) - overlay some values
5643      * @return {Roo.data.Record} record created.
5644      */
5645     newRow :  function(d) {
5646         var da =  {};
5647         this.recordType.prototype.fields.each(function(c) {
5648             switch( c.type) {
5649                 case 'int' : da[c.name] = 0; break;
5650                 case 'date' : da[c.name] = new Date(); break;
5651                 case 'float' : da[c.name] = 0.0; break;
5652                 case 'boolean' : da[c.name] = false; break;
5653                 default : da[c.name] = ""; break;
5654             }
5655             
5656         });
5657         return new this.recordType(Roo.apply(da, d));
5658     }
5659     
5660 };/*
5661  * Based on:
5662  * Ext JS Library 1.1.1
5663  * Copyright(c) 2006-2007, Ext JS, LLC.
5664  *
5665  * Originally Released Under LGPL - original licence link has changed is not relivant.
5666  *
5667  * Fork - LGPL
5668  * <script type="text/javascript">
5669  */
5670
5671 /**
5672  * @class Roo.data.DataProxy
5673  * @extends Roo.data.Observable
5674  * This class is an abstract base class for implementations which provide retrieval of
5675  * unformatted data objects.<br>
5676  * <p>
5677  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5678  * (of the appropriate type which knows how to parse the data object) to provide a block of
5679  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5680  * <p>
5681  * Custom implementations must implement the load method as described in
5682  * {@link Roo.data.HttpProxy#load}.
5683  */
5684 Roo.data.DataProxy = function(){
5685     this.addEvents({
5686         /**
5687          * @event beforeload
5688          * Fires before a network request is made to retrieve a data object.
5689          * @param {Object} This DataProxy object.
5690          * @param {Object} params The params parameter to the load function.
5691          */
5692         beforeload : true,
5693         /**
5694          * @event load
5695          * Fires before the load method's callback is called.
5696          * @param {Object} This DataProxy object.
5697          * @param {Object} o The data object.
5698          * @param {Object} arg The callback argument object passed to the load function.
5699          */
5700         load : true,
5701         /**
5702          * @event loadexception
5703          * Fires if an Exception occurs during data retrieval.
5704          * @param {Object} This DataProxy object.
5705          * @param {Object} o The data object.
5706          * @param {Object} arg The callback argument object passed to the load function.
5707          * @param {Object} e The Exception.
5708          */
5709         loadexception : true
5710     });
5711     Roo.data.DataProxy.superclass.constructor.call(this);
5712 };
5713
5714 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5715
5716     /**
5717      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5718      */
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.MemoryProxy
5731  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5732  * to the Reader when its load method is called.
5733  * @constructor
5734  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5735  */
5736 Roo.data.MemoryProxy = function(data){
5737     if (data.data) {
5738         data = data.data;
5739     }
5740     Roo.data.MemoryProxy.superclass.constructor.call(this);
5741     this.data = data;
5742 };
5743
5744 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5745     /**
5746      * Load data from the requested source (in this case an in-memory
5747      * data object passed to the constructor), read the data object into
5748      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5749      * process that block using the passed callback.
5750      * @param {Object} params This parameter is not used by the MemoryProxy class.
5751      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5752      * object into a block of Roo.data.Records.
5753      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5754      * The function must be passed <ul>
5755      * <li>The Record block object</li>
5756      * <li>The "arg" argument from the load function</li>
5757      * <li>A boolean success indicator</li>
5758      * </ul>
5759      * @param {Object} scope The scope in which to call the callback
5760      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5761      */
5762     load : function(params, reader, callback, scope, arg){
5763         params = params || {};
5764         var result;
5765         try {
5766             result = reader.readRecords(this.data);
5767         }catch(e){
5768             this.fireEvent("loadexception", this, arg, null, e);
5769             callback.call(scope, null, arg, false);
5770             return;
5771         }
5772         callback.call(scope, result, arg, true);
5773     },
5774     
5775     // private
5776     update : function(params, records){
5777         
5778     }
5779 });/*
5780  * Based on:
5781  * Ext JS Library 1.1.1
5782  * Copyright(c) 2006-2007, Ext JS, LLC.
5783  *
5784  * Originally Released Under LGPL - original licence link has changed is not relivant.
5785  *
5786  * Fork - LGPL
5787  * <script type="text/javascript">
5788  */
5789 /**
5790  * @class Roo.data.HttpProxy
5791  * @extends Roo.data.DataProxy
5792  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5793  * configured to reference a certain URL.<br><br>
5794  * <p>
5795  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5796  * from which the running page was served.<br><br>
5797  * <p>
5798  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5799  * <p>
5800  * Be aware that to enable the browser to parse an XML document, the server must set
5801  * the Content-Type header in the HTTP response to "text/xml".
5802  * @constructor
5803  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5804  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5805  * will be used to make the request.
5806  */
5807 Roo.data.HttpProxy = function(conn){
5808     Roo.data.HttpProxy.superclass.constructor.call(this);
5809     // is conn a conn config or a real conn?
5810     this.conn = conn;
5811     this.useAjax = !conn || !conn.events;
5812   
5813 };
5814
5815 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5816     // thse are take from connection...
5817     
5818     /**
5819      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5823      * extra parameters to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5827      *  to each request made by this object. (defaults to undefined)
5828      */
5829     /**
5830      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5831      */
5832     /**
5833      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5834      */
5835      /**
5836      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5837      * @type Boolean
5838      */
5839   
5840
5841     /**
5842      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5843      * @type Boolean
5844      */
5845     /**
5846      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5847      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5848      * a finer-grained basis than the DataProxy events.
5849      */
5850     getConnection : function(){
5851         return this.useAjax ? Roo.Ajax : this.conn;
5852     },
5853
5854     /**
5855      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5856      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5857      * process that block using the passed callback.
5858      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5859      * for the request to the remote server.
5860      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5861      * object into a block of Roo.data.Records.
5862      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5863      * The function must be passed <ul>
5864      * <li>The Record block object</li>
5865      * <li>The "arg" argument from the load function</li>
5866      * <li>A boolean success indicator</li>
5867      * </ul>
5868      * @param {Object} scope The scope in which to call the callback
5869      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5870      */
5871     load : function(params, reader, callback, scope, arg){
5872         if(this.fireEvent("beforeload", this, params) !== false){
5873             var  o = {
5874                 params : params || {},
5875                 request: {
5876                     callback : callback,
5877                     scope : scope,
5878                     arg : arg
5879                 },
5880                 reader: reader,
5881                 callback : this.loadResponse,
5882                 scope: this
5883             };
5884             if(this.useAjax){
5885                 Roo.applyIf(o, this.conn);
5886                 if(this.activeRequest){
5887                     Roo.Ajax.abort(this.activeRequest);
5888                 }
5889                 this.activeRequest = Roo.Ajax.request(o);
5890             }else{
5891                 this.conn.request(o);
5892             }
5893         }else{
5894             callback.call(scope||this, null, arg, false);
5895         }
5896     },
5897
5898     // private
5899     loadResponse : function(o, success, response){
5900         delete this.activeRequest;
5901         if(!success){
5902             this.fireEvent("loadexception", this, o, response);
5903             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5904             return;
5905         }
5906         var result;
5907         try {
5908             result = o.reader.read(response);
5909         }catch(e){
5910             this.fireEvent("loadexception", this, o, response, e);
5911             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5912             return;
5913         }
5914         
5915         this.fireEvent("load", this, o, o.request.arg);
5916         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5917     },
5918
5919     // private
5920     update : function(dataSet){
5921
5922     },
5923
5924     // private
5925     updateResponse : function(dataSet){
5926
5927     }
5928 });/*
5929  * Based on:
5930  * Ext JS Library 1.1.1
5931  * Copyright(c) 2006-2007, Ext JS, LLC.
5932  *
5933  * Originally Released Under LGPL - original licence link has changed is not relivant.
5934  *
5935  * Fork - LGPL
5936  * <script type="text/javascript">
5937  */
5938
5939 /**
5940  * @class Roo.data.ScriptTagProxy
5941  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5942  * other than the originating domain of the running page.<br><br>
5943  * <p>
5944  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5945  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5946  * <p>
5947  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5948  * source code that is used as the source inside a &lt;script> tag.<br><br>
5949  * <p>
5950  * In order for the browser to process the returned data, the server must wrap the data object
5951  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5952  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5953  * depending on whether the callback name was passed:
5954  * <p>
5955  * <pre><code>
5956 boolean scriptTag = false;
5957 String cb = request.getParameter("callback");
5958 if (cb != null) {
5959     scriptTag = true;
5960     response.setContentType("text/javascript");
5961 } else {
5962     response.setContentType("application/x-json");
5963 }
5964 Writer out = response.getWriter();
5965 if (scriptTag) {
5966     out.write(cb + "(");
5967 }
5968 out.print(dataBlock.toJsonString());
5969 if (scriptTag) {
5970     out.write(");");
5971 }
5972 </pre></code>
5973  *
5974  * @constructor
5975  * @param {Object} config A configuration object.
5976  */
5977 Roo.data.ScriptTagProxy = function(config){
5978     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5979     Roo.apply(this, config);
5980     this.head = document.getElementsByTagName("head")[0];
5981 };
5982
5983 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5984
5985 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5986     /**
5987      * @cfg {String} url The URL from which to request the data object.
5988      */
5989     /**
5990      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5991      */
5992     timeout : 30000,
5993     /**
5994      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5995      * the server the name of the callback function set up by the load call to process the returned data object.
5996      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5997      * javascript output which calls this named function passing the data object as its only parameter.
5998      */
5999     callbackParam : "callback",
6000     /**
6001      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
6002      * name to the request.
6003      */
6004     nocache : true,
6005
6006     /**
6007      * Load data from the configured URL, read the data object into
6008      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6009      * process that block using the passed callback.
6010      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6011      * for the request to the remote server.
6012      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6013      * object into a block of Roo.data.Records.
6014      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6015      * The function must be passed <ul>
6016      * <li>The Record block object</li>
6017      * <li>The "arg" argument from the load function</li>
6018      * <li>A boolean success indicator</li>
6019      * </ul>
6020      * @param {Object} scope The scope in which to call the callback
6021      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6022      */
6023     load : function(params, reader, callback, scope, arg){
6024         if(this.fireEvent("beforeload", this, params) !== false){
6025
6026             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6027
6028             var url = this.url;
6029             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6030             if(this.nocache){
6031                 url += "&_dc=" + (new Date().getTime());
6032             }
6033             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6034             var trans = {
6035                 id : transId,
6036                 cb : "stcCallback"+transId,
6037                 scriptId : "stcScript"+transId,
6038                 params : params,
6039                 arg : arg,
6040                 url : url,
6041                 callback : callback,
6042                 scope : scope,
6043                 reader : reader
6044             };
6045             var conn = this;
6046
6047             window[trans.cb] = function(o){
6048                 conn.handleResponse(o, trans);
6049             };
6050
6051             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6052
6053             if(this.autoAbort !== false){
6054                 this.abort();
6055             }
6056
6057             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6058
6059             var script = document.createElement("script");
6060             script.setAttribute("src", url);
6061             script.setAttribute("type", "text/javascript");
6062             script.setAttribute("id", trans.scriptId);
6063             this.head.appendChild(script);
6064
6065             this.trans = trans;
6066         }else{
6067             callback.call(scope||this, null, arg, false);
6068         }
6069     },
6070
6071     // private
6072     isLoading : function(){
6073         return this.trans ? true : false;
6074     },
6075
6076     /**
6077      * Abort the current server request.
6078      */
6079     abort : function(){
6080         if(this.isLoading()){
6081             this.destroyTrans(this.trans);
6082         }
6083     },
6084
6085     // private
6086     destroyTrans : function(trans, isLoaded){
6087         this.head.removeChild(document.getElementById(trans.scriptId));
6088         clearTimeout(trans.timeoutId);
6089         if(isLoaded){
6090             window[trans.cb] = undefined;
6091             try{
6092                 delete window[trans.cb];
6093             }catch(e){}
6094         }else{
6095             // if hasn't been loaded, wait for load to remove it to prevent script error
6096             window[trans.cb] = function(){
6097                 window[trans.cb] = undefined;
6098                 try{
6099                     delete window[trans.cb];
6100                 }catch(e){}
6101             };
6102         }
6103     },
6104
6105     // private
6106     handleResponse : function(o, trans){
6107         this.trans = false;
6108         this.destroyTrans(trans, true);
6109         var result;
6110         try {
6111             result = trans.reader.readRecords(o);
6112         }catch(e){
6113             this.fireEvent("loadexception", this, o, trans.arg, e);
6114             trans.callback.call(trans.scope||window, null, trans.arg, false);
6115             return;
6116         }
6117         this.fireEvent("load", this, o, trans.arg);
6118         trans.callback.call(trans.scope||window, result, trans.arg, true);
6119     },
6120
6121     // private
6122     handleFailure : function(trans){
6123         this.trans = false;
6124         this.destroyTrans(trans, false);
6125         this.fireEvent("loadexception", this, null, trans.arg);
6126         trans.callback.call(trans.scope||window, null, trans.arg, false);
6127     }
6128 });/*
6129  * Based on:
6130  * Ext JS Library 1.1.1
6131  * Copyright(c) 2006-2007, Ext JS, LLC.
6132  *
6133  * Originally Released Under LGPL - original licence link has changed is not relivant.
6134  *
6135  * Fork - LGPL
6136  * <script type="text/javascript">
6137  */
6138
6139 /**
6140  * @class Roo.data.JsonReader
6141  * @extends Roo.data.DataReader
6142  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6143  * based on mappings in a provided Roo.data.Record constructor.
6144  * 
6145  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6146  * in the reply previously. 
6147  * 
6148  * <p>
6149  * Example code:
6150  * <pre><code>
6151 var RecordDef = Roo.data.Record.create([
6152     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6153     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6154 ]);
6155 var myReader = new Roo.data.JsonReader({
6156     totalProperty: "results",    // The property which contains the total dataset size (optional)
6157     root: "rows",                // The property which contains an Array of row objects
6158     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6159 }, RecordDef);
6160 </code></pre>
6161  * <p>
6162  * This would consume a JSON file like this:
6163  * <pre><code>
6164 { 'results': 2, 'rows': [
6165     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6166     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6167 }
6168 </code></pre>
6169  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6170  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6171  * paged from the remote server.
6172  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6173  * @cfg {String} root name of the property which contains the Array of row objects.
6174  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6175  * @cfg {Array} fields Array of field definition objects
6176  * @constructor
6177  * Create a new JsonReader
6178  * @param {Object} meta Metadata configuration options
6179  * @param {Object} recordType Either an Array of field definition objects,
6180  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6181  */
6182 Roo.data.JsonReader = function(meta, recordType){
6183     
6184     meta = meta || {};
6185     // set some defaults:
6186     Roo.applyIf(meta, {
6187         totalProperty: 'total',
6188         successProperty : 'success',
6189         root : 'data',
6190         id : 'id'
6191     });
6192     
6193     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6194 };
6195 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6196     
6197     /**
6198      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6199      * Used by Store query builder to append _requestMeta to params.
6200      * 
6201      */
6202     metaFromRemote : false,
6203     /**
6204      * This method is only used by a DataProxy which has retrieved data from a remote server.
6205      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6206      * @return {Object} data A data block which is used by an Roo.data.Store object as
6207      * a cache of Roo.data.Records.
6208      */
6209     read : function(response){
6210         var json = response.responseText;
6211        
6212         var o = /* eval:var:o */ eval("("+json+")");
6213         if(!o) {
6214             throw {message: "JsonReader.read: Json object not found"};
6215         }
6216         
6217         if(o.metaData){
6218             
6219             delete this.ef;
6220             this.metaFromRemote = true;
6221             this.meta = o.metaData;
6222             this.recordType = Roo.data.Record.create(o.metaData.fields);
6223             this.onMetaChange(this.meta, this.recordType, o);
6224         }
6225         return this.readRecords(o);
6226     },
6227
6228     // private function a store will implement
6229     onMetaChange : function(meta, recordType, o){
6230
6231     },
6232
6233     /**
6234          * @ignore
6235          */
6236     simpleAccess: function(obj, subsc) {
6237         return obj[subsc];
6238     },
6239
6240         /**
6241          * @ignore
6242          */
6243     getJsonAccessor: function(){
6244         var re = /[\[\.]/;
6245         return function(expr) {
6246             try {
6247                 return(re.test(expr))
6248                     ? new Function("obj", "return obj." + expr)
6249                     : function(obj){
6250                         return obj[expr];
6251                     };
6252             } catch(e){}
6253             return Roo.emptyFn;
6254         };
6255     }(),
6256
6257     /**
6258      * Create a data block containing Roo.data.Records from an XML document.
6259      * @param {Object} o An object which contains an Array of row objects in the property specified
6260      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6261      * which contains the total size of the dataset.
6262      * @return {Object} data A data block which is used by an Roo.data.Store object as
6263      * a cache of Roo.data.Records.
6264      */
6265     readRecords : function(o){
6266         /**
6267          * After any data loads, the raw JSON data is available for further custom processing.
6268          * @type Object
6269          */
6270         this.o = o;
6271         var s = this.meta, Record = this.recordType,
6272             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6273
6274 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6275         if (!this.ef) {
6276             if(s.totalProperty) {
6277                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6278                 }
6279                 if(s.successProperty) {
6280                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6281                 }
6282                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6283                 if (s.id) {
6284                         var g = this.getJsonAccessor(s.id);
6285                         this.getId = function(rec) {
6286                                 var r = g(rec);  
6287                                 return (r === undefined || r === "") ? null : r;
6288                         };
6289                 } else {
6290                         this.getId = function(){return null;};
6291                 }
6292             this.ef = [];
6293             for(var jj = 0; jj < fl; jj++){
6294                 f = fi[jj];
6295                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6296                 this.ef[jj] = this.getJsonAccessor(map);
6297             }
6298         }
6299
6300         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6301         if(s.totalProperty){
6302             var vt = parseInt(this.getTotal(o), 10);
6303             if(!isNaN(vt)){
6304                 totalRecords = vt;
6305             }
6306         }
6307         if(s.successProperty){
6308             var vs = this.getSuccess(o);
6309             if(vs === false || vs === 'false'){
6310                 success = false;
6311             }
6312         }
6313         var records = [];
6314         for(var i = 0; i < c; i++){
6315                 var n = root[i];
6316             var values = {};
6317             var id = this.getId(n);
6318             for(var j = 0; j < fl; j++){
6319                 f = fi[j];
6320             var v = this.ef[j](n);
6321             if (!f.convert) {
6322                 Roo.log('missing convert for ' + f.name);
6323                 Roo.log(f);
6324                 continue;
6325             }
6326             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6327             }
6328             var record = new Record(values, id);
6329             record.json = n;
6330             records[i] = record;
6331         }
6332         return {
6333             raw : o,
6334             success : success,
6335             records : records,
6336             totalRecords : totalRecords
6337         };
6338     }
6339 });/*
6340  * Based on:
6341  * Ext JS Library 1.1.1
6342  * Copyright(c) 2006-2007, Ext JS, LLC.
6343  *
6344  * Originally Released Under LGPL - original licence link has changed is not relivant.
6345  *
6346  * Fork - LGPL
6347  * <script type="text/javascript">
6348  */
6349
6350 /**
6351  * @class Roo.data.XmlReader
6352  * @extends Roo.data.DataReader
6353  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6354  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6355  * <p>
6356  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6357  * header in the HTTP response must be set to "text/xml".</em>
6358  * <p>
6359  * Example code:
6360  * <pre><code>
6361 var RecordDef = Roo.data.Record.create([
6362    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6363    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6364 ]);
6365 var myReader = new Roo.data.XmlReader({
6366    totalRecords: "results", // The element which contains the total dataset size (optional)
6367    record: "row",           // The repeated element which contains row information
6368    id: "id"                 // The element within the row that provides an ID for the record (optional)
6369 }, RecordDef);
6370 </code></pre>
6371  * <p>
6372  * This would consume an XML file like this:
6373  * <pre><code>
6374 &lt;?xml?>
6375 &lt;dataset>
6376  &lt;results>2&lt;/results>
6377  &lt;row>
6378    &lt;id>1&lt;/id>
6379    &lt;name>Bill&lt;/name>
6380    &lt;occupation>Gardener&lt;/occupation>
6381  &lt;/row>
6382  &lt;row>
6383    &lt;id>2&lt;/id>
6384    &lt;name>Ben&lt;/name>
6385    &lt;occupation>Horticulturalist&lt;/occupation>
6386  &lt;/row>
6387 &lt;/dataset>
6388 </code></pre>
6389  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6390  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6391  * paged from the remote server.
6392  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6393  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6394  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6395  * a record identifier value.
6396  * @constructor
6397  * Create a new XmlReader
6398  * @param {Object} meta Metadata configuration options
6399  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6400  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6401  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6402  */
6403 Roo.data.XmlReader = function(meta, recordType){
6404     meta = meta || {};
6405     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6406 };
6407 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6408     /**
6409      * This method is only used by a DataProxy which has retrieved data from a remote server.
6410          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6411          * to contain a method called 'responseXML' that returns an XML document object.
6412      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6413      * a cache of Roo.data.Records.
6414      */
6415     read : function(response){
6416         var doc = response.responseXML;
6417         if(!doc) {
6418             throw {message: "XmlReader.read: XML Document not available"};
6419         }
6420         return this.readRecords(doc);
6421     },
6422
6423     /**
6424      * Create a data block containing Roo.data.Records from an XML document.
6425          * @param {Object} doc A parsed XML document.
6426      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6427      * a cache of Roo.data.Records.
6428      */
6429     readRecords : function(doc){
6430         /**
6431          * After any data loads/reads, the raw XML Document is available for further custom processing.
6432          * @type XMLDocument
6433          */
6434         this.xmlData = doc;
6435         var root = doc.documentElement || doc;
6436         var q = Roo.DomQuery;
6437         var recordType = this.recordType, fields = recordType.prototype.fields;
6438         var sid = this.meta.id;
6439         var totalRecords = 0, success = true;
6440         if(this.meta.totalRecords){
6441             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6442         }
6443         
6444         if(this.meta.success){
6445             var sv = q.selectValue(this.meta.success, root, true);
6446             success = sv !== false && sv !== 'false';
6447         }
6448         var records = [];
6449         var ns = q.select(this.meta.record, root);
6450         for(var i = 0, len = ns.length; i < len; i++) {
6451                 var n = ns[i];
6452                 var values = {};
6453                 var id = sid ? q.selectValue(sid, n) : undefined;
6454                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6455                     var f = fields.items[j];
6456                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6457                     v = f.convert(v);
6458                     values[f.name] = v;
6459                 }
6460                 var record = new recordType(values, id);
6461                 record.node = n;
6462                 records[records.length] = record;
6463             }
6464
6465             return {
6466                 success : success,
6467                 records : records,
6468                 totalRecords : totalRecords || records.length
6469             };
6470     }
6471 });/*
6472  * Based on:
6473  * Ext JS Library 1.1.1
6474  * Copyright(c) 2006-2007, Ext JS, LLC.
6475  *
6476  * Originally Released Under LGPL - original licence link has changed is not relivant.
6477  *
6478  * Fork - LGPL
6479  * <script type="text/javascript">
6480  */
6481
6482 /**
6483  * @class Roo.data.ArrayReader
6484  * @extends Roo.data.DataReader
6485  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6486  * Each element of that Array represents a row of data fields. The
6487  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6488  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6489  * <p>
6490  * Example code:.
6491  * <pre><code>
6492 var RecordDef = Roo.data.Record.create([
6493     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6494     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6495 ]);
6496 var myReader = new Roo.data.ArrayReader({
6497     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6498 }, RecordDef);
6499 </code></pre>
6500  * <p>
6501  * This would consume an Array like this:
6502  * <pre><code>
6503 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6504   </code></pre>
6505  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6506  * @constructor
6507  * Create a new JsonReader
6508  * @param {Object} meta Metadata configuration options.
6509  * @param {Object} recordType Either an Array of field definition objects
6510  * as specified to {@link Roo.data.Record#create},
6511  * or an {@link Roo.data.Record} object
6512  * created using {@link Roo.data.Record#create}.
6513  */
6514 Roo.data.ArrayReader = function(meta, recordType){
6515     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6516 };
6517
6518 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6519     /**
6520      * Create a data block containing Roo.data.Records from an XML document.
6521      * @param {Object} o An Array of row objects which represents the dataset.
6522      * @return {Object} data A data block which is used by an Roo.data.Store object as
6523      * a cache of Roo.data.Records.
6524      */
6525     readRecords : function(o){
6526         var sid = this.meta ? this.meta.id : null;
6527         var recordType = this.recordType, fields = recordType.prototype.fields;
6528         var records = [];
6529         var root = o;
6530             for(var i = 0; i < root.length; i++){
6531                     var n = root[i];
6532                 var values = {};
6533                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6534                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6535                 var f = fields.items[j];
6536                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6537                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6538                 v = f.convert(v);
6539                 values[f.name] = v;
6540             }
6541                 var record = new recordType(values, id);
6542                 record.json = n;
6543                 records[records.length] = record;
6544             }
6545             return {
6546                 records : records,
6547                 totalRecords : records.length
6548             };
6549     }
6550 });/*
6551  * Based on:
6552  * Ext JS Library 1.1.1
6553  * Copyright(c) 2006-2007, Ext JS, LLC.
6554  *
6555  * Originally Released Under LGPL - original licence link has changed is not relivant.
6556  *
6557  * Fork - LGPL
6558  * <script type="text/javascript">
6559  */
6560
6561
6562 /**
6563  * @class Roo.data.Tree
6564  * @extends Roo.util.Observable
6565  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6566  * in the tree have most standard DOM functionality.
6567  * @constructor
6568  * @param {Node} root (optional) The root node
6569  */
6570 Roo.data.Tree = function(root){
6571    this.nodeHash = {};
6572    /**
6573     * The root node for this tree
6574     * @type Node
6575     */
6576    this.root = null;
6577    if(root){
6578        this.setRootNode(root);
6579    }
6580    this.addEvents({
6581        /**
6582         * @event append
6583         * Fires when a new child node is appended to a node in this tree.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The newly appended node
6587         * @param {Number} index The index of the newly appended node
6588         */
6589        "append" : true,
6590        /**
6591         * @event remove
6592         * Fires when a child node is removed from a node in this tree.
6593         * @param {Tree} tree The owner tree
6594         * @param {Node} parent The parent node
6595         * @param {Node} node The child node removed
6596         */
6597        "remove" : true,
6598        /**
6599         * @event move
6600         * Fires when a node is moved to a new location in the tree
6601         * @param {Tree} tree The owner tree
6602         * @param {Node} node The node moved
6603         * @param {Node} oldParent The old parent of this node
6604         * @param {Node} newParent The new parent of this node
6605         * @param {Number} index The index it was moved to
6606         */
6607        "move" : true,
6608        /**
6609         * @event insert
6610         * Fires when a new child node is inserted in a node in this tree.
6611         * @param {Tree} tree The owner tree
6612         * @param {Node} parent The parent node
6613         * @param {Node} node The child node inserted
6614         * @param {Node} refNode The child node the node was inserted before
6615         */
6616        "insert" : true,
6617        /**
6618         * @event beforeappend
6619         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6620         * @param {Tree} tree The owner tree
6621         * @param {Node} parent The parent node
6622         * @param {Node} node The child node to be appended
6623         */
6624        "beforeappend" : true,
6625        /**
6626         * @event beforeremove
6627         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6628         * @param {Tree} tree The owner tree
6629         * @param {Node} parent The parent node
6630         * @param {Node} node The child node to be removed
6631         */
6632        "beforeremove" : true,
6633        /**
6634         * @event beforemove
6635         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6636         * @param {Tree} tree The owner tree
6637         * @param {Node} node The node being moved
6638         * @param {Node} oldParent The parent of the node
6639         * @param {Node} newParent The new parent the node is moving to
6640         * @param {Number} index The index it is being moved to
6641         */
6642        "beforemove" : true,
6643        /**
6644         * @event beforeinsert
6645         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6646         * @param {Tree} tree The owner tree
6647         * @param {Node} parent The parent node
6648         * @param {Node} node The child node to be inserted
6649         * @param {Node} refNode The child node the node is being inserted before
6650         */
6651        "beforeinsert" : true
6652    });
6653
6654     Roo.data.Tree.superclass.constructor.call(this);
6655 };
6656
6657 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6658     pathSeparator: "/",
6659
6660     proxyNodeEvent : function(){
6661         return this.fireEvent.apply(this, arguments);
6662     },
6663
6664     /**
6665      * Returns the root node for this tree.
6666      * @return {Node}
6667      */
6668     getRootNode : function(){
6669         return this.root;
6670     },
6671
6672     /**
6673      * Sets the root node for this tree.
6674      * @param {Node} node
6675      * @return {Node}
6676      */
6677     setRootNode : function(node){
6678         this.root = node;
6679         node.ownerTree = this;
6680         node.isRoot = true;
6681         this.registerNode(node);
6682         return node;
6683     },
6684
6685     /**
6686      * Gets a node in this tree by its id.
6687      * @param {String} id
6688      * @return {Node}
6689      */
6690     getNodeById : function(id){
6691         return this.nodeHash[id];
6692     },
6693
6694     registerNode : function(node){
6695         this.nodeHash[node.id] = node;
6696     },
6697
6698     unregisterNode : function(node){
6699         delete this.nodeHash[node.id];
6700     },
6701
6702     toString : function(){
6703         return "[Tree"+(this.id?" "+this.id:"")+"]";
6704     }
6705 });
6706
6707 /**
6708  * @class Roo.data.Node
6709  * @extends Roo.util.Observable
6710  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6711  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6712  * @constructor
6713  * @param {Object} attributes The attributes/config for the node
6714  */
6715 Roo.data.Node = function(attributes){
6716     /**
6717      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6718      * @type {Object}
6719      */
6720     this.attributes = attributes || {};
6721     this.leaf = this.attributes.leaf;
6722     /**
6723      * The node id. @type String
6724      */
6725     this.id = this.attributes.id;
6726     if(!this.id){
6727         this.id = Roo.id(null, "ynode-");
6728         this.attributes.id = this.id;
6729     }
6730      
6731     
6732     /**
6733      * All child nodes of this node. @type Array
6734      */
6735     this.childNodes = [];
6736     if(!this.childNodes.indexOf){ // indexOf is a must
6737         this.childNodes.indexOf = function(o){
6738             for(var i = 0, len = this.length; i < len; i++){
6739                 if(this[i] == o) {
6740                     return i;
6741                 }
6742             }
6743             return -1;
6744         };
6745     }
6746     /**
6747      * The parent node for this node. @type Node
6748      */
6749     this.parentNode = null;
6750     /**
6751      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6752      */
6753     this.firstChild = null;
6754     /**
6755      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6756      */
6757     this.lastChild = null;
6758     /**
6759      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6760      */
6761     this.previousSibling = null;
6762     /**
6763      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6764      */
6765     this.nextSibling = null;
6766
6767     this.addEvents({
6768        /**
6769         * @event append
6770         * Fires when a new child node is appended
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} node The newly appended node
6774         * @param {Number} index The index of the newly appended node
6775         */
6776        "append" : true,
6777        /**
6778         * @event remove
6779         * Fires when a child node is removed
6780         * @param {Tree} tree The owner tree
6781         * @param {Node} this This node
6782         * @param {Node} node The removed node
6783         */
6784        "remove" : true,
6785        /**
6786         * @event move
6787         * Fires when this node is moved to a new location in the tree
6788         * @param {Tree} tree The owner tree
6789         * @param {Node} this This node
6790         * @param {Node} oldParent The old parent of this node
6791         * @param {Node} newParent The new parent of this node
6792         * @param {Number} index The index it was moved to
6793         */
6794        "move" : true,
6795        /**
6796         * @event insert
6797         * Fires when a new child node is inserted.
6798         * @param {Tree} tree The owner tree
6799         * @param {Node} this This node
6800         * @param {Node} node The child node inserted
6801         * @param {Node} refNode The child node the node was inserted before
6802         */
6803        "insert" : true,
6804        /**
6805         * @event beforeappend
6806         * Fires before a new child is appended, return false to cancel the append.
6807         * @param {Tree} tree The owner tree
6808         * @param {Node} this This node
6809         * @param {Node} node The child node to be appended
6810         */
6811        "beforeappend" : true,
6812        /**
6813         * @event beforeremove
6814         * Fires before a child is removed, return false to cancel the remove.
6815         * @param {Tree} tree The owner tree
6816         * @param {Node} this This node
6817         * @param {Node} node The child node to be removed
6818         */
6819        "beforeremove" : true,
6820        /**
6821         * @event beforemove
6822         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6823         * @param {Tree} tree The owner tree
6824         * @param {Node} this This node
6825         * @param {Node} oldParent The parent of this node
6826         * @param {Node} newParent The new parent this node is moving to
6827         * @param {Number} index The index it is being moved to
6828         */
6829        "beforemove" : true,
6830        /**
6831         * @event beforeinsert
6832         * Fires before a new child is inserted, return false to cancel the insert.
6833         * @param {Tree} tree The owner tree
6834         * @param {Node} this This node
6835         * @param {Node} node The child node to be inserted
6836         * @param {Node} refNode The child node the node is being inserted before
6837         */
6838        "beforeinsert" : true
6839    });
6840     this.listeners = this.attributes.listeners;
6841     Roo.data.Node.superclass.constructor.call(this);
6842 };
6843
6844 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6845     fireEvent : function(evtName){
6846         // first do standard event for this node
6847         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6848             return false;
6849         }
6850         // then bubble it up to the tree if the event wasn't cancelled
6851         var ot = this.getOwnerTree();
6852         if(ot){
6853             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6854                 return false;
6855             }
6856         }
6857         return true;
6858     },
6859
6860     /**
6861      * Returns true if this node is a leaf
6862      * @return {Boolean}
6863      */
6864     isLeaf : function(){
6865         return this.leaf === true;
6866     },
6867
6868     // private
6869     setFirstChild : function(node){
6870         this.firstChild = node;
6871     },
6872
6873     //private
6874     setLastChild : function(node){
6875         this.lastChild = node;
6876     },
6877
6878
6879     /**
6880      * Returns true if this node is the last child of its parent
6881      * @return {Boolean}
6882      */
6883     isLast : function(){
6884        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6885     },
6886
6887     /**
6888      * Returns true if this node is the first child of its parent
6889      * @return {Boolean}
6890      */
6891     isFirst : function(){
6892        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6893     },
6894
6895     hasChildNodes : function(){
6896         return !this.isLeaf() && this.childNodes.length > 0;
6897     },
6898
6899     /**
6900      * Insert node(s) as the last child node of this node.
6901      * @param {Node/Array} node The node or Array of nodes to append
6902      * @return {Node} The appended node if single append, or null if an array was passed
6903      */
6904     appendChild : function(node){
6905         var multi = false;
6906         if(node instanceof Array){
6907             multi = node;
6908         }else if(arguments.length > 1){
6909             multi = arguments;
6910         }
6911         // if passed an array or multiple args do them one by one
6912         if(multi){
6913             for(var i = 0, len = multi.length; i < len; i++) {
6914                 this.appendChild(multi[i]);
6915             }
6916         }else{
6917             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6918                 return false;
6919             }
6920             var index = this.childNodes.length;
6921             var oldParent = node.parentNode;
6922             // it's a move, make sure we move it cleanly
6923             if(oldParent){
6924                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6925                     return false;
6926                 }
6927                 oldParent.removeChild(node);
6928             }
6929             index = this.childNodes.length;
6930             if(index == 0){
6931                 this.setFirstChild(node);
6932             }
6933             this.childNodes.push(node);
6934             node.parentNode = this;
6935             var ps = this.childNodes[index-1];
6936             if(ps){
6937                 node.previousSibling = ps;
6938                 ps.nextSibling = node;
6939             }else{
6940                 node.previousSibling = null;
6941             }
6942             node.nextSibling = null;
6943             this.setLastChild(node);
6944             node.setOwnerTree(this.getOwnerTree());
6945             this.fireEvent("append", this.ownerTree, this, node, index);
6946             if(oldParent){
6947                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6948             }
6949             return node;
6950         }
6951     },
6952
6953     /**
6954      * Removes a child node from this node.
6955      * @param {Node} node The node to remove
6956      * @return {Node} The removed node
6957      */
6958     removeChild : function(node){
6959         var index = this.childNodes.indexOf(node);
6960         if(index == -1){
6961             return false;
6962         }
6963         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6964             return false;
6965         }
6966
6967         // remove it from childNodes collection
6968         this.childNodes.splice(index, 1);
6969
6970         // update siblings
6971         if(node.previousSibling){
6972             node.previousSibling.nextSibling = node.nextSibling;
6973         }
6974         if(node.nextSibling){
6975             node.nextSibling.previousSibling = node.previousSibling;
6976         }
6977
6978         // update child refs
6979         if(this.firstChild == node){
6980             this.setFirstChild(node.nextSibling);
6981         }
6982         if(this.lastChild == node){
6983             this.setLastChild(node.previousSibling);
6984         }
6985
6986         node.setOwnerTree(null);
6987         // clear any references from the node
6988         node.parentNode = null;
6989         node.previousSibling = null;
6990         node.nextSibling = null;
6991         this.fireEvent("remove", this.ownerTree, this, node);
6992         return node;
6993     },
6994
6995     /**
6996      * Inserts the first node before the second node in this nodes childNodes collection.
6997      * @param {Node} node The node to insert
6998      * @param {Node} refNode The node to insert before (if null the node is appended)
6999      * @return {Node} The inserted node
7000      */
7001     insertBefore : function(node, refNode){
7002         if(!refNode){ // like standard Dom, refNode can be null for append
7003             return this.appendChild(node);
7004         }
7005         // nothing to do
7006         if(node == refNode){
7007             return false;
7008         }
7009
7010         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7011             return false;
7012         }
7013         var index = this.childNodes.indexOf(refNode);
7014         var oldParent = node.parentNode;
7015         var refIndex = index;
7016
7017         // when moving internally, indexes will change after remove
7018         if(oldParent == this && this.childNodes.indexOf(node) < index){
7019             refIndex--;
7020         }
7021
7022         // it's a move, make sure we move it cleanly
7023         if(oldParent){
7024             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7025                 return false;
7026             }
7027             oldParent.removeChild(node);
7028         }
7029         if(refIndex == 0){
7030             this.setFirstChild(node);
7031         }
7032         this.childNodes.splice(refIndex, 0, node);
7033         node.parentNode = this;
7034         var ps = this.childNodes[refIndex-1];
7035         if(ps){
7036             node.previousSibling = ps;
7037             ps.nextSibling = node;
7038         }else{
7039             node.previousSibling = null;
7040         }
7041         node.nextSibling = refNode;
7042         refNode.previousSibling = node;
7043         node.setOwnerTree(this.getOwnerTree());
7044         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7045         if(oldParent){
7046             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7047         }
7048         return node;
7049     },
7050
7051     /**
7052      * Returns the child node at the specified index.
7053      * @param {Number} index
7054      * @return {Node}
7055      */
7056     item : function(index){
7057         return this.childNodes[index];
7058     },
7059
7060     /**
7061      * Replaces one child node in this node with another.
7062      * @param {Node} newChild The replacement node
7063      * @param {Node} oldChild The node to replace
7064      * @return {Node} The replaced node
7065      */
7066     replaceChild : function(newChild, oldChild){
7067         this.insertBefore(newChild, oldChild);
7068         this.removeChild(oldChild);
7069         return oldChild;
7070     },
7071
7072     /**
7073      * Returns the index of a child node
7074      * @param {Node} node
7075      * @return {Number} The index of the node or -1 if it was not found
7076      */
7077     indexOf : function(child){
7078         return this.childNodes.indexOf(child);
7079     },
7080
7081     /**
7082      * Returns the tree this node is in.
7083      * @return {Tree}
7084      */
7085     getOwnerTree : function(){
7086         // if it doesn't have one, look for one
7087         if(!this.ownerTree){
7088             var p = this;
7089             while(p){
7090                 if(p.ownerTree){
7091                     this.ownerTree = p.ownerTree;
7092                     break;
7093                 }
7094                 p = p.parentNode;
7095             }
7096         }
7097         return this.ownerTree;
7098     },
7099
7100     /**
7101      * Returns depth of this node (the root node has a depth of 0)
7102      * @return {Number}
7103      */
7104     getDepth : function(){
7105         var depth = 0;
7106         var p = this;
7107         while(p.parentNode){
7108             ++depth;
7109             p = p.parentNode;
7110         }
7111         return depth;
7112     },
7113
7114     // private
7115     setOwnerTree : function(tree){
7116         // if it's move, we need to update everyone
7117         if(tree != this.ownerTree){
7118             if(this.ownerTree){
7119                 this.ownerTree.unregisterNode(this);
7120             }
7121             this.ownerTree = tree;
7122             var cs = this.childNodes;
7123             for(var i = 0, len = cs.length; i < len; i++) {
7124                 cs[i].setOwnerTree(tree);
7125             }
7126             if(tree){
7127                 tree.registerNode(this);
7128             }
7129         }
7130     },
7131
7132     /**
7133      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7134      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7135      * @return {String} The path
7136      */
7137     getPath : function(attr){
7138         attr = attr || "id";
7139         var p = this.parentNode;
7140         var b = [this.attributes[attr]];
7141         while(p){
7142             b.unshift(p.attributes[attr]);
7143             p = p.parentNode;
7144         }
7145         var sep = this.getOwnerTree().pathSeparator;
7146         return sep + b.join(sep);
7147     },
7148
7149     /**
7150      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7151      * function call will be the scope provided or the current node. The arguments to the function
7152      * will be the args provided or the current node. If the function returns false at any point,
7153      * the bubble is stopped.
7154      * @param {Function} fn The function to call
7155      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7156      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7157      */
7158     bubble : function(fn, scope, args){
7159         var p = this;
7160         while(p){
7161             if(fn.call(scope || p, args || p) === false){
7162                 break;
7163             }
7164             p = p.parentNode;
7165         }
7166     },
7167
7168     /**
7169      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7170      * function call will be the scope provided or the current node. The arguments to the function
7171      * will be the args provided or the current node. If the function returns false at any point,
7172      * the cascade is stopped on that branch.
7173      * @param {Function} fn The function to call
7174      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7175      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7176      */
7177     cascade : function(fn, scope, args){
7178         if(fn.call(scope || this, args || this) !== false){
7179             var cs = this.childNodes;
7180             for(var i = 0, len = cs.length; i < len; i++) {
7181                 cs[i].cascade(fn, scope, args);
7182             }
7183         }
7184     },
7185
7186     /**
7187      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7188      * function call will be the scope provided or the current node. The arguments to the function
7189      * will be the args provided or the current node. If the function returns false at any point,
7190      * the iteration stops.
7191      * @param {Function} fn The function to call
7192      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7193      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7194      */
7195     eachChild : function(fn, scope, args){
7196         var cs = this.childNodes;
7197         for(var i = 0, len = cs.length; i < len; i++) {
7198                 if(fn.call(scope || this, args || cs[i]) === false){
7199                     break;
7200                 }
7201         }
7202     },
7203
7204     /**
7205      * Finds the first child that has the attribute with the specified value.
7206      * @param {String} attribute The attribute name
7207      * @param {Mixed} value The value to search for
7208      * @return {Node} The found child or null if none was found
7209      */
7210     findChild : function(attribute, value){
7211         var cs = this.childNodes;
7212         for(var i = 0, len = cs.length; i < len; i++) {
7213                 if(cs[i].attributes[attribute] == value){
7214                     return cs[i];
7215                 }
7216         }
7217         return null;
7218     },
7219
7220     /**
7221      * Finds the first child by a custom function. The child matches if the function passed
7222      * returns true.
7223      * @param {Function} fn
7224      * @param {Object} scope (optional)
7225      * @return {Node} The found child or null if none was found
7226      */
7227     findChildBy : function(fn, scope){
7228         var cs = this.childNodes;
7229         for(var i = 0, len = cs.length; i < len; i++) {
7230                 if(fn.call(scope||cs[i], cs[i]) === true){
7231                     return cs[i];
7232                 }
7233         }
7234         return null;
7235     },
7236
7237     /**
7238      * Sorts this nodes children using the supplied sort function
7239      * @param {Function} fn
7240      * @param {Object} scope (optional)
7241      */
7242     sort : function(fn, scope){
7243         var cs = this.childNodes;
7244         var len = cs.length;
7245         if(len > 0){
7246             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7247             cs.sort(sortFn);
7248             for(var i = 0; i < len; i++){
7249                 var n = cs[i];
7250                 n.previousSibling = cs[i-1];
7251                 n.nextSibling = cs[i+1];
7252                 if(i == 0){
7253                     this.setFirstChild(n);
7254                 }
7255                 if(i == len-1){
7256                     this.setLastChild(n);
7257                 }
7258             }
7259         }
7260     },
7261
7262     /**
7263      * Returns true if this node is an ancestor (at any point) of the passed node.
7264      * @param {Node} node
7265      * @return {Boolean}
7266      */
7267     contains : function(node){
7268         return node.isAncestor(this);
7269     },
7270
7271     /**
7272      * Returns true if the passed node is an ancestor (at any point) of this node.
7273      * @param {Node} node
7274      * @return {Boolean}
7275      */
7276     isAncestor : function(node){
7277         var p = this.parentNode;
7278         while(p){
7279             if(p == node){
7280                 return true;
7281             }
7282             p = p.parentNode;
7283         }
7284         return false;
7285     },
7286
7287     toString : function(){
7288         return "[Node"+(this.id?" "+this.id:"")+"]";
7289     }
7290 });/*
7291  * Based on:
7292  * Ext JS Library 1.1.1
7293  * Copyright(c) 2006-2007, Ext JS, LLC.
7294  *
7295  * Originally Released Under LGPL - original licence link has changed is not relivant.
7296  *
7297  * Fork - LGPL
7298  * <script type="text/javascript">
7299  */
7300  (function(){ 
7301 /**
7302  * @class Roo.Layer
7303  * @extends Roo.Element
7304  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7305  * automatic maintaining of shadow/shim positions.
7306  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7307  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7308  * you can pass a string with a CSS class name. False turns off the shadow.
7309  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7310  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7311  * @cfg {String} cls CSS class to add to the element
7312  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7313  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7314  * @constructor
7315  * @param {Object} config An object with config options.
7316  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7317  */
7318
7319 Roo.Layer = function(config, existingEl){
7320     config = config || {};
7321     var dh = Roo.DomHelper;
7322     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7323     if(existingEl){
7324         this.dom = Roo.getDom(existingEl);
7325     }
7326     if(!this.dom){
7327         var o = config.dh || {tag: "div", cls: "x-layer"};
7328         this.dom = dh.append(pel, o);
7329     }
7330     if(config.cls){
7331         this.addClass(config.cls);
7332     }
7333     this.constrain = config.constrain !== false;
7334     this.visibilityMode = Roo.Element.VISIBILITY;
7335     if(config.id){
7336         this.id = this.dom.id = config.id;
7337     }else{
7338         this.id = Roo.id(this.dom);
7339     }
7340     this.zindex = config.zindex || this.getZIndex();
7341     this.position("absolute", this.zindex);
7342     if(config.shadow){
7343         this.shadowOffset = config.shadowOffset || 4;
7344         this.shadow = new Roo.Shadow({
7345             offset : this.shadowOffset,
7346             mode : config.shadow
7347         });
7348     }else{
7349         this.shadowOffset = 0;
7350     }
7351     this.useShim = config.shim !== false && Roo.useShims;
7352     this.useDisplay = config.useDisplay;
7353     this.hide();
7354 };
7355
7356 var supr = Roo.Element.prototype;
7357
7358 // shims are shared among layer to keep from having 100 iframes
7359 var shims = [];
7360
7361 Roo.extend(Roo.Layer, Roo.Element, {
7362
7363     getZIndex : function(){
7364         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7365     },
7366
7367     getShim : function(){
7368         if(!this.useShim){
7369             return null;
7370         }
7371         if(this.shim){
7372             return this.shim;
7373         }
7374         var shim = shims.shift();
7375         if(!shim){
7376             shim = this.createShim();
7377             shim.enableDisplayMode('block');
7378             shim.dom.style.display = 'none';
7379             shim.dom.style.visibility = 'visible';
7380         }
7381         var pn = this.dom.parentNode;
7382         if(shim.dom.parentNode != pn){
7383             pn.insertBefore(shim.dom, this.dom);
7384         }
7385         shim.setStyle('z-index', this.getZIndex()-2);
7386         this.shim = shim;
7387         return shim;
7388     },
7389
7390     hideShim : function(){
7391         if(this.shim){
7392             this.shim.setDisplayed(false);
7393             shims.push(this.shim);
7394             delete this.shim;
7395         }
7396     },
7397
7398     disableShadow : function(){
7399         if(this.shadow){
7400             this.shadowDisabled = true;
7401             this.shadow.hide();
7402             this.lastShadowOffset = this.shadowOffset;
7403             this.shadowOffset = 0;
7404         }
7405     },
7406
7407     enableShadow : function(show){
7408         if(this.shadow){
7409             this.shadowDisabled = false;
7410             this.shadowOffset = this.lastShadowOffset;
7411             delete this.lastShadowOffset;
7412             if(show){
7413                 this.sync(true);
7414             }
7415         }
7416     },
7417
7418     // private
7419     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7420     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7421     sync : function(doShow){
7422         var sw = this.shadow;
7423         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7424             var sh = this.getShim();
7425
7426             var w = this.getWidth(),
7427                 h = this.getHeight();
7428
7429             var l = this.getLeft(true),
7430                 t = this.getTop(true);
7431
7432             if(sw && !this.shadowDisabled){
7433                 if(doShow && !sw.isVisible()){
7434                     sw.show(this);
7435                 }else{
7436                     sw.realign(l, t, w, h);
7437                 }
7438                 if(sh){
7439                     if(doShow){
7440                        sh.show();
7441                     }
7442                     // fit the shim behind the shadow, so it is shimmed too
7443                     var a = sw.adjusts, s = sh.dom.style;
7444                     s.left = (Math.min(l, l+a.l))+"px";
7445                     s.top = (Math.min(t, t+a.t))+"px";
7446                     s.width = (w+a.w)+"px";
7447                     s.height = (h+a.h)+"px";
7448                 }
7449             }else if(sh){
7450                 if(doShow){
7451                    sh.show();
7452                 }
7453                 sh.setSize(w, h);
7454                 sh.setLeftTop(l, t);
7455             }
7456             
7457         }
7458     },
7459
7460     // private
7461     destroy : function(){
7462         this.hideShim();
7463         if(this.shadow){
7464             this.shadow.hide();
7465         }
7466         this.removeAllListeners();
7467         var pn = this.dom.parentNode;
7468         if(pn){
7469             pn.removeChild(this.dom);
7470         }
7471         Roo.Element.uncache(this.id);
7472     },
7473
7474     remove : function(){
7475         this.destroy();
7476     },
7477
7478     // private
7479     beginUpdate : function(){
7480         this.updating = true;
7481     },
7482
7483     // private
7484     endUpdate : function(){
7485         this.updating = false;
7486         this.sync(true);
7487     },
7488
7489     // private
7490     hideUnders : function(negOffset){
7491         if(this.shadow){
7492             this.shadow.hide();
7493         }
7494         this.hideShim();
7495     },
7496
7497     // private
7498     constrainXY : function(){
7499         if(this.constrain){
7500             var vw = Roo.lib.Dom.getViewWidth(),
7501                 vh = Roo.lib.Dom.getViewHeight();
7502             var s = Roo.get(document).getScroll();
7503
7504             var xy = this.getXY();
7505             var x = xy[0], y = xy[1];   
7506             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7507             // only move it if it needs it
7508             var moved = false;
7509             // first validate right/bottom
7510             if((x + w) > vw+s.left){
7511                 x = vw - w - this.shadowOffset;
7512                 moved = true;
7513             }
7514             if((y + h) > vh+s.top){
7515                 y = vh - h - this.shadowOffset;
7516                 moved = true;
7517             }
7518             // then make sure top/left isn't negative
7519             if(x < s.left){
7520                 x = s.left;
7521                 moved = true;
7522             }
7523             if(y < s.top){
7524                 y = s.top;
7525                 moved = true;
7526             }
7527             if(moved){
7528                 if(this.avoidY){
7529                     var ay = this.avoidY;
7530                     if(y <= ay && (y+h) >= ay){
7531                         y = ay-h-5;   
7532                     }
7533                 }
7534                 xy = [x, y];
7535                 this.storeXY(xy);
7536                 supr.setXY.call(this, xy);
7537                 this.sync();
7538             }
7539         }
7540     },
7541
7542     isVisible : function(){
7543         return this.visible;    
7544     },
7545
7546     // private
7547     showAction : function(){
7548         this.visible = true; // track visibility to prevent getStyle calls
7549         if(this.useDisplay === true){
7550             this.setDisplayed("");
7551         }else if(this.lastXY){
7552             supr.setXY.call(this, this.lastXY);
7553         }else if(this.lastLT){
7554             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7555         }
7556     },
7557
7558     // private
7559     hideAction : function(){
7560         this.visible = false;
7561         if(this.useDisplay === true){
7562             this.setDisplayed(false);
7563         }else{
7564             this.setLeftTop(-10000,-10000);
7565         }
7566     },
7567
7568     // overridden Element method
7569     setVisible : function(v, a, d, c, e){
7570         if(v){
7571             this.showAction();
7572         }
7573         if(a && v){
7574             var cb = function(){
7575                 this.sync(true);
7576                 if(c){
7577                     c();
7578                 }
7579             }.createDelegate(this);
7580             supr.setVisible.call(this, true, true, d, cb, e);
7581         }else{
7582             if(!v){
7583                 this.hideUnders(true);
7584             }
7585             var cb = c;
7586             if(a){
7587                 cb = function(){
7588                     this.hideAction();
7589                     if(c){
7590                         c();
7591                     }
7592                 }.createDelegate(this);
7593             }
7594             supr.setVisible.call(this, v, a, d, cb, e);
7595             if(v){
7596                 this.sync(true);
7597             }else if(!a){
7598                 this.hideAction();
7599             }
7600         }
7601     },
7602
7603     storeXY : function(xy){
7604         delete this.lastLT;
7605         this.lastXY = xy;
7606     },
7607
7608     storeLeftTop : function(left, top){
7609         delete this.lastXY;
7610         this.lastLT = [left, top];
7611     },
7612
7613     // private
7614     beforeFx : function(){
7615         this.beforeAction();
7616         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7617     },
7618
7619     // private
7620     afterFx : function(){
7621         Roo.Layer.superclass.afterFx.apply(this, arguments);
7622         this.sync(this.isVisible());
7623     },
7624
7625     // private
7626     beforeAction : function(){
7627         if(!this.updating && this.shadow){
7628             this.shadow.hide();
7629         }
7630     },
7631
7632     // overridden Element method
7633     setLeft : function(left){
7634         this.storeLeftTop(left, this.getTop(true));
7635         supr.setLeft.apply(this, arguments);
7636         this.sync();
7637     },
7638
7639     setTop : function(top){
7640         this.storeLeftTop(this.getLeft(true), top);
7641         supr.setTop.apply(this, arguments);
7642         this.sync();
7643     },
7644
7645     setLeftTop : function(left, top){
7646         this.storeLeftTop(left, top);
7647         supr.setLeftTop.apply(this, arguments);
7648         this.sync();
7649     },
7650
7651     setXY : function(xy, a, d, c, e){
7652         this.fixDisplay();
7653         this.beforeAction();
7654         this.storeXY(xy);
7655         var cb = this.createCB(c);
7656         supr.setXY.call(this, xy, a, d, cb, e);
7657         if(!a){
7658             cb();
7659         }
7660     },
7661
7662     // private
7663     createCB : function(c){
7664         var el = this;
7665         return function(){
7666             el.constrainXY();
7667             el.sync(true);
7668             if(c){
7669                 c();
7670             }
7671         };
7672     },
7673
7674     // overridden Element method
7675     setX : function(x, a, d, c, e){
7676         this.setXY([x, this.getY()], a, d, c, e);
7677     },
7678
7679     // overridden Element method
7680     setY : function(y, a, d, c, e){
7681         this.setXY([this.getX(), y], a, d, c, e);
7682     },
7683
7684     // overridden Element method
7685     setSize : function(w, h, a, d, c, e){
7686         this.beforeAction();
7687         var cb = this.createCB(c);
7688         supr.setSize.call(this, w, h, a, d, cb, e);
7689         if(!a){
7690             cb();
7691         }
7692     },
7693
7694     // overridden Element method
7695     setWidth : function(w, a, d, c, e){
7696         this.beforeAction();
7697         var cb = this.createCB(c);
7698         supr.setWidth.call(this, w, a, d, cb, e);
7699         if(!a){
7700             cb();
7701         }
7702     },
7703
7704     // overridden Element method
7705     setHeight : function(h, a, d, c, e){
7706         this.beforeAction();
7707         var cb = this.createCB(c);
7708         supr.setHeight.call(this, h, a, d, cb, e);
7709         if(!a){
7710             cb();
7711         }
7712     },
7713
7714     // overridden Element method
7715     setBounds : function(x, y, w, h, a, d, c, e){
7716         this.beforeAction();
7717         var cb = this.createCB(c);
7718         if(!a){
7719             this.storeXY([x, y]);
7720             supr.setXY.call(this, [x, y]);
7721             supr.setSize.call(this, w, h, a, d, cb, e);
7722             cb();
7723         }else{
7724             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7725         }
7726         return this;
7727     },
7728     
7729     /**
7730      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7731      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7732      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7733      * @param {Number} zindex The new z-index to set
7734      * @return {this} The Layer
7735      */
7736     setZIndex : function(zindex){
7737         this.zindex = zindex;
7738         this.setStyle("z-index", zindex + 2);
7739         if(this.shadow){
7740             this.shadow.setZIndex(zindex + 1);
7741         }
7742         if(this.shim){
7743             this.shim.setStyle("z-index", zindex);
7744         }
7745     }
7746 });
7747 })();/*
7748  * Based on:
7749  * Ext JS Library 1.1.1
7750  * Copyright(c) 2006-2007, Ext JS, LLC.
7751  *
7752  * Originally Released Under LGPL - original licence link has changed is not relivant.
7753  *
7754  * Fork - LGPL
7755  * <script type="text/javascript">
7756  */
7757
7758
7759 /**
7760  * @class Roo.Shadow
7761  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7762  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7763  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7764  * @constructor
7765  * Create a new Shadow
7766  * @param {Object} config The config object
7767  */
7768 Roo.Shadow = function(config){
7769     Roo.apply(this, config);
7770     if(typeof this.mode != "string"){
7771         this.mode = this.defaultMode;
7772     }
7773     var o = this.offset, a = {h: 0};
7774     var rad = Math.floor(this.offset/2);
7775     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7776         case "drop":
7777             a.w = 0;
7778             a.l = a.t = o;
7779             a.t -= 1;
7780             if(Roo.isIE){
7781                 a.l -= this.offset + rad;
7782                 a.t -= this.offset + rad;
7783                 a.w -= rad;
7784                 a.h -= rad;
7785                 a.t += 1;
7786             }
7787         break;
7788         case "sides":
7789             a.w = (o*2);
7790             a.l = -o;
7791             a.t = o-1;
7792             if(Roo.isIE){
7793                 a.l -= (this.offset - rad);
7794                 a.t -= this.offset + rad;
7795                 a.l += 1;
7796                 a.w -= (this.offset - rad)*2;
7797                 a.w -= rad + 1;
7798                 a.h -= 1;
7799             }
7800         break;
7801         case "frame":
7802             a.w = a.h = (o*2);
7803             a.l = a.t = -o;
7804             a.t += 1;
7805             a.h -= 2;
7806             if(Roo.isIE){
7807                 a.l -= (this.offset - rad);
7808                 a.t -= (this.offset - rad);
7809                 a.l += 1;
7810                 a.w -= (this.offset + rad + 1);
7811                 a.h -= (this.offset + rad);
7812                 a.h += 1;
7813             }
7814         break;
7815     };
7816
7817     this.adjusts = a;
7818 };
7819
7820 Roo.Shadow.prototype = {
7821     /**
7822      * @cfg {String} mode
7823      * The shadow display mode.  Supports the following options:<br />
7824      * sides: Shadow displays on both sides and bottom only<br />
7825      * frame: Shadow displays equally on all four sides<br />
7826      * drop: Traditional bottom-right drop shadow (default)
7827      */
7828     /**
7829      * @cfg {String} offset
7830      * The number of pixels to offset the shadow from the element (defaults to 4)
7831      */
7832     offset: 4,
7833
7834     // private
7835     defaultMode: "drop",
7836
7837     /**
7838      * Displays the shadow under the target element
7839      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7840      */
7841     show : function(target){
7842         target = Roo.get(target);
7843         if(!this.el){
7844             this.el = Roo.Shadow.Pool.pull();
7845             if(this.el.dom.nextSibling != target.dom){
7846                 this.el.insertBefore(target);
7847             }
7848         }
7849         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7850         if(Roo.isIE){
7851             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7852         }
7853         this.realign(
7854             target.getLeft(true),
7855             target.getTop(true),
7856             target.getWidth(),
7857             target.getHeight()
7858         );
7859         this.el.dom.style.display = "block";
7860     },
7861
7862     /**
7863      * Returns true if the shadow is visible, else false
7864      */
7865     isVisible : function(){
7866         return this.el ? true : false;  
7867     },
7868
7869     /**
7870      * Direct alignment when values are already available. Show must be called at least once before
7871      * calling this method to ensure it is initialized.
7872      * @param {Number} left The target element left position
7873      * @param {Number} top The target element top position
7874      * @param {Number} width The target element width
7875      * @param {Number} height The target element height
7876      */
7877     realign : function(l, t, w, h){
7878         if(!this.el){
7879             return;
7880         }
7881         var a = this.adjusts, d = this.el.dom, s = d.style;
7882         var iea = 0;
7883         s.left = (l+a.l)+"px";
7884         s.top = (t+a.t)+"px";
7885         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7886  
7887         if(s.width != sws || s.height != shs){
7888             s.width = sws;
7889             s.height = shs;
7890             if(!Roo.isIE){
7891                 var cn = d.childNodes;
7892                 var sww = Math.max(0, (sw-12))+"px";
7893                 cn[0].childNodes[1].style.width = sww;
7894                 cn[1].childNodes[1].style.width = sww;
7895                 cn[2].childNodes[1].style.width = sww;
7896                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7897             }
7898         }
7899     },
7900
7901     /**
7902      * Hides this shadow
7903      */
7904     hide : function(){
7905         if(this.el){
7906             this.el.dom.style.display = "none";
7907             Roo.Shadow.Pool.push(this.el);
7908             delete this.el;
7909         }
7910     },
7911
7912     /**
7913      * Adjust the z-index of this shadow
7914      * @param {Number} zindex The new z-index
7915      */
7916     setZIndex : function(z){
7917         this.zIndex = z;
7918         if(this.el){
7919             this.el.setStyle("z-index", z);
7920         }
7921     }
7922 };
7923
7924 // Private utility class that manages the internal Shadow cache
7925 Roo.Shadow.Pool = function(){
7926     var p = [];
7927     var markup = Roo.isIE ?
7928                  '<div class="x-ie-shadow"></div>' :
7929                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
7930     return {
7931         pull : function(){
7932             var sh = p.shift();
7933             if(!sh){
7934                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7935                 sh.autoBoxAdjust = false;
7936             }
7937             return sh;
7938         },
7939
7940         push : function(sh){
7941             p.push(sh);
7942         }
7943     };
7944 }();/*
7945  * Based on:
7946  * Ext JS Library 1.1.1
7947  * Copyright(c) 2006-2007, Ext JS, LLC.
7948  *
7949  * Originally Released Under LGPL - original licence link has changed is not relivant.
7950  *
7951  * Fork - LGPL
7952  * <script type="text/javascript">
7953  */
7954
7955
7956 /**
7957  * @class Roo.SplitBar
7958  * @extends Roo.util.Observable
7959  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7960  * <br><br>
7961  * Usage:
7962  * <pre><code>
7963 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7964                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7965 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7966 split.minSize = 100;
7967 split.maxSize = 600;
7968 split.animate = true;
7969 split.on('moved', splitterMoved);
7970 </code></pre>
7971  * @constructor
7972  * Create a new SplitBar
7973  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7974  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7975  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7976  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7977                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7978                         position of the SplitBar).
7979  */
7980 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7981     
7982     /** @private */
7983     this.el = Roo.get(dragElement, true);
7984     this.el.dom.unselectable = "on";
7985     /** @private */
7986     this.resizingEl = Roo.get(resizingElement, true);
7987
7988     /**
7989      * @private
7990      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7991      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7992      * @type Number
7993      */
7994     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7995     
7996     /**
7997      * The minimum size of the resizing element. (Defaults to 0)
7998      * @type Number
7999      */
8000     this.minSize = 0;
8001     
8002     /**
8003      * The maximum size of the resizing element. (Defaults to 2000)
8004      * @type Number
8005      */
8006     this.maxSize = 2000;
8007     
8008     /**
8009      * Whether to animate the transition to the new size
8010      * @type Boolean
8011      */
8012     this.animate = false;
8013     
8014     /**
8015      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8016      * @type Boolean
8017      */
8018     this.useShim = false;
8019     
8020     /** @private */
8021     this.shim = null;
8022     
8023     if(!existingProxy){
8024         /** @private */
8025         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8026     }else{
8027         this.proxy = Roo.get(existingProxy).dom;
8028     }
8029     /** @private */
8030     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8031     
8032     /** @private */
8033     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8034     
8035     /** @private */
8036     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8037     
8038     /** @private */
8039     this.dragSpecs = {};
8040     
8041     /**
8042      * @private The adapter to use to positon and resize elements
8043      */
8044     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8045     this.adapter.init(this);
8046     
8047     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8048         /** @private */
8049         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8050         this.el.addClass("x-splitbar-h");
8051     }else{
8052         /** @private */
8053         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8054         this.el.addClass("x-splitbar-v");
8055     }
8056     
8057     this.addEvents({
8058         /**
8059          * @event resize
8060          * Fires when the splitter is moved (alias for {@link #event-moved})
8061          * @param {Roo.SplitBar} this
8062          * @param {Number} newSize the new width or height
8063          */
8064         "resize" : true,
8065         /**
8066          * @event moved
8067          * Fires when the splitter is moved
8068          * @param {Roo.SplitBar} this
8069          * @param {Number} newSize the new width or height
8070          */
8071         "moved" : true,
8072         /**
8073          * @event beforeresize
8074          * Fires before the splitter is dragged
8075          * @param {Roo.SplitBar} this
8076          */
8077         "beforeresize" : true,
8078
8079         "beforeapply" : true
8080     });
8081
8082     Roo.util.Observable.call(this);
8083 };
8084
8085 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8086     onStartProxyDrag : function(x, y){
8087         this.fireEvent("beforeresize", this);
8088         if(!this.overlay){
8089             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8090             o.unselectable();
8091             o.enableDisplayMode("block");
8092             // all splitbars share the same overlay
8093             Roo.SplitBar.prototype.overlay = o;
8094         }
8095         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8096         this.overlay.show();
8097         Roo.get(this.proxy).setDisplayed("block");
8098         var size = this.adapter.getElementSize(this);
8099         this.activeMinSize = this.getMinimumSize();;
8100         this.activeMaxSize = this.getMaximumSize();;
8101         var c1 = size - this.activeMinSize;
8102         var c2 = Math.max(this.activeMaxSize - size, 0);
8103         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8104             this.dd.resetConstraints();
8105             this.dd.setXConstraint(
8106                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8107                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8108             );
8109             this.dd.setYConstraint(0, 0);
8110         }else{
8111             this.dd.resetConstraints();
8112             this.dd.setXConstraint(0, 0);
8113             this.dd.setYConstraint(
8114                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8115                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8116             );
8117          }
8118         this.dragSpecs.startSize = size;
8119         this.dragSpecs.startPoint = [x, y];
8120         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8121     },
8122     
8123     /** 
8124      * @private Called after the drag operation by the DDProxy
8125      */
8126     onEndProxyDrag : function(e){
8127         Roo.get(this.proxy).setDisplayed(false);
8128         var endPoint = Roo.lib.Event.getXY(e);
8129         if(this.overlay){
8130             this.overlay.hide();
8131         }
8132         var newSize;
8133         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8134             newSize = this.dragSpecs.startSize + 
8135                 (this.placement == Roo.SplitBar.LEFT ?
8136                     endPoint[0] - this.dragSpecs.startPoint[0] :
8137                     this.dragSpecs.startPoint[0] - endPoint[0]
8138                 );
8139         }else{
8140             newSize = this.dragSpecs.startSize + 
8141                 (this.placement == Roo.SplitBar.TOP ?
8142                     endPoint[1] - this.dragSpecs.startPoint[1] :
8143                     this.dragSpecs.startPoint[1] - endPoint[1]
8144                 );
8145         }
8146         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8147         if(newSize != this.dragSpecs.startSize){
8148             if(this.fireEvent('beforeapply', this, newSize) !== false){
8149                 this.adapter.setElementSize(this, newSize);
8150                 this.fireEvent("moved", this, newSize);
8151                 this.fireEvent("resize", this, newSize);
8152             }
8153         }
8154     },
8155     
8156     /**
8157      * Get the adapter this SplitBar uses
8158      * @return The adapter object
8159      */
8160     getAdapter : function(){
8161         return this.adapter;
8162     },
8163     
8164     /**
8165      * Set the adapter this SplitBar uses
8166      * @param {Object} adapter A SplitBar adapter object
8167      */
8168     setAdapter : function(adapter){
8169         this.adapter = adapter;
8170         this.adapter.init(this);
8171     },
8172     
8173     /**
8174      * Gets the minimum size for the resizing element
8175      * @return {Number} The minimum size
8176      */
8177     getMinimumSize : function(){
8178         return this.minSize;
8179     },
8180     
8181     /**
8182      * Sets the minimum size for the resizing element
8183      * @param {Number} minSize The minimum size
8184      */
8185     setMinimumSize : function(minSize){
8186         this.minSize = minSize;
8187     },
8188     
8189     /**
8190      * Gets the maximum size for the resizing element
8191      * @return {Number} The maximum size
8192      */
8193     getMaximumSize : function(){
8194         return this.maxSize;
8195     },
8196     
8197     /**
8198      * Sets the maximum size for the resizing element
8199      * @param {Number} maxSize The maximum size
8200      */
8201     setMaximumSize : function(maxSize){
8202         this.maxSize = maxSize;
8203     },
8204     
8205     /**
8206      * Sets the initialize size for the resizing element
8207      * @param {Number} size The initial size
8208      */
8209     setCurrentSize : function(size){
8210         var oldAnimate = this.animate;
8211         this.animate = false;
8212         this.adapter.setElementSize(this, size);
8213         this.animate = oldAnimate;
8214     },
8215     
8216     /**
8217      * Destroy this splitbar. 
8218      * @param {Boolean} removeEl True to remove the element
8219      */
8220     destroy : function(removeEl){
8221         if(this.shim){
8222             this.shim.remove();
8223         }
8224         this.dd.unreg();
8225         this.proxy.parentNode.removeChild(this.proxy);
8226         if(removeEl){
8227             this.el.remove();
8228         }
8229     }
8230 });
8231
8232 /**
8233  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8234  */
8235 Roo.SplitBar.createProxy = function(dir){
8236     var proxy = new Roo.Element(document.createElement("div"));
8237     proxy.unselectable();
8238     var cls = 'x-splitbar-proxy';
8239     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8240     document.body.appendChild(proxy.dom);
8241     return proxy.dom;
8242 };
8243
8244 /** 
8245  * @class Roo.SplitBar.BasicLayoutAdapter
8246  * Default Adapter. It assumes the splitter and resizing element are not positioned
8247  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8248  */
8249 Roo.SplitBar.BasicLayoutAdapter = function(){
8250 };
8251
8252 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8253     // do nothing for now
8254     init : function(s){
8255     
8256     },
8257     /**
8258      * Called before drag operations to get the current size of the resizing element. 
8259      * @param {Roo.SplitBar} s The SplitBar using this adapter
8260      */
8261      getElementSize : function(s){
8262         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8263             return s.resizingEl.getWidth();
8264         }else{
8265             return s.resizingEl.getHeight();
8266         }
8267     },
8268     
8269     /**
8270      * Called after drag operations to set the size of the resizing element.
8271      * @param {Roo.SplitBar} s The SplitBar using this adapter
8272      * @param {Number} newSize The new size to set
8273      * @param {Function} onComplete A function to be invoked when resizing is complete
8274      */
8275     setElementSize : function(s, newSize, onComplete){
8276         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8277             if(!s.animate){
8278                 s.resizingEl.setWidth(newSize);
8279                 if(onComplete){
8280                     onComplete(s, newSize);
8281                 }
8282             }else{
8283                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8284             }
8285         }else{
8286             
8287             if(!s.animate){
8288                 s.resizingEl.setHeight(newSize);
8289                 if(onComplete){
8290                     onComplete(s, newSize);
8291                 }
8292             }else{
8293                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8294             }
8295         }
8296     }
8297 };
8298
8299 /** 
8300  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8301  * @extends Roo.SplitBar.BasicLayoutAdapter
8302  * Adapter that  moves the splitter element to align with the resized sizing element. 
8303  * Used with an absolute positioned SplitBar.
8304  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8305  * document.body, make sure you assign an id to the body element.
8306  */
8307 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8308     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8309     this.container = Roo.get(container);
8310 };
8311
8312 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8313     init : function(s){
8314         this.basic.init(s);
8315     },
8316     
8317     getElementSize : function(s){
8318         return this.basic.getElementSize(s);
8319     },
8320     
8321     setElementSize : function(s, newSize, onComplete){
8322         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8323     },
8324     
8325     moveSplitter : function(s){
8326         var yes = Roo.SplitBar;
8327         switch(s.placement){
8328             case yes.LEFT:
8329                 s.el.setX(s.resizingEl.getRight());
8330                 break;
8331             case yes.RIGHT:
8332                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8333                 break;
8334             case yes.TOP:
8335                 s.el.setY(s.resizingEl.getBottom());
8336                 break;
8337             case yes.BOTTOM:
8338                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8339                 break;
8340         }
8341     }
8342 };
8343
8344 /**
8345  * Orientation constant - Create a vertical SplitBar
8346  * @static
8347  * @type Number
8348  */
8349 Roo.SplitBar.VERTICAL = 1;
8350
8351 /**
8352  * Orientation constant - Create a horizontal SplitBar
8353  * @static
8354  * @type Number
8355  */
8356 Roo.SplitBar.HORIZONTAL = 2;
8357
8358 /**
8359  * Placement constant - The resizing element is to the left of the splitter element
8360  * @static
8361  * @type Number
8362  */
8363 Roo.SplitBar.LEFT = 1;
8364
8365 /**
8366  * Placement constant - The resizing element is to the right of the splitter element
8367  * @static
8368  * @type Number
8369  */
8370 Roo.SplitBar.RIGHT = 2;
8371
8372 /**
8373  * Placement constant - The resizing element is positioned above the splitter element
8374  * @static
8375  * @type Number
8376  */
8377 Roo.SplitBar.TOP = 3;
8378
8379 /**
8380  * Placement constant - The resizing element is positioned under splitter element
8381  * @static
8382  * @type Number
8383  */
8384 Roo.SplitBar.BOTTOM = 4;
8385 /*
8386  * Based on:
8387  * Ext JS Library 1.1.1
8388  * Copyright(c) 2006-2007, Ext JS, LLC.
8389  *
8390  * Originally Released Under LGPL - original licence link has changed is not relivant.
8391  *
8392  * Fork - LGPL
8393  * <script type="text/javascript">
8394  */
8395
8396 /**
8397  * @class Roo.View
8398  * @extends Roo.util.Observable
8399  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8400  * This class also supports single and multi selection modes. <br>
8401  * Create a data model bound view:
8402  <pre><code>
8403  var store = new Roo.data.Store(...);
8404
8405  var view = new Roo.View({
8406     el : "my-element",
8407     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8408  
8409     singleSelect: true,
8410     selectedClass: "ydataview-selected",
8411     store: store
8412  });
8413
8414  // listen for node click?
8415  view.on("click", function(vw, index, node, e){
8416  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8417  });
8418
8419  // load XML data
8420  dataModel.load("foobar.xml");
8421  </code></pre>
8422  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8423  * <br><br>
8424  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8425  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8426  * 
8427  * Note: old style constructor is still suported (container, template, config)
8428  * 
8429  * @constructor
8430  * Create a new View
8431  * @param {Object} config The config object
8432  * 
8433  */
8434 Roo.View = function(config, depreciated_tpl, depreciated_config){
8435     
8436     this.parent = false;
8437     
8438     if (typeof(depreciated_tpl) == 'undefined') {
8439         // new way.. - universal constructor.
8440         Roo.apply(this, config);
8441         this.el  = Roo.get(this.el);
8442     } else {
8443         // old format..
8444         this.el  = Roo.get(config);
8445         this.tpl = depreciated_tpl;
8446         Roo.apply(this, depreciated_config);
8447     }
8448     this.wrapEl  = this.el.wrap().wrap();
8449     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8450     
8451     
8452     if(typeof(this.tpl) == "string"){
8453         this.tpl = new Roo.Template(this.tpl);
8454     } else {
8455         // support xtype ctors..
8456         this.tpl = new Roo.factory(this.tpl, Roo);
8457     }
8458     
8459     
8460     this.tpl.compile();
8461     
8462     /** @private */
8463     this.addEvents({
8464         /**
8465          * @event beforeclick
8466          * Fires before a click is processed. Returns false to cancel the default action.
8467          * @param {Roo.View} this
8468          * @param {Number} index The index of the target node
8469          * @param {HTMLElement} node The target node
8470          * @param {Roo.EventObject} e The raw event object
8471          */
8472             "beforeclick" : true,
8473         /**
8474          * @event click
8475          * Fires when a template node is clicked.
8476          * @param {Roo.View} this
8477          * @param {Number} index The index of the target node
8478          * @param {HTMLElement} node The target node
8479          * @param {Roo.EventObject} e The raw event object
8480          */
8481             "click" : true,
8482         /**
8483          * @event dblclick
8484          * Fires when a template node is double clicked.
8485          * @param {Roo.View} this
8486          * @param {Number} index The index of the target node
8487          * @param {HTMLElement} node The target node
8488          * @param {Roo.EventObject} e The raw event object
8489          */
8490             "dblclick" : true,
8491         /**
8492          * @event contextmenu
8493          * Fires when a template node is right clicked.
8494          * @param {Roo.View} this
8495          * @param {Number} index The index of the target node
8496          * @param {HTMLElement} node The target node
8497          * @param {Roo.EventObject} e The raw event object
8498          */
8499             "contextmenu" : true,
8500         /**
8501          * @event selectionchange
8502          * Fires when the selected nodes change.
8503          * @param {Roo.View} this
8504          * @param {Array} selections Array of the selected nodes
8505          */
8506             "selectionchange" : true,
8507     
8508         /**
8509          * @event beforeselect
8510          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8511          * @param {Roo.View} this
8512          * @param {HTMLElement} node The node to be selected
8513          * @param {Array} selections Array of currently selected nodes
8514          */
8515             "beforeselect" : true,
8516         /**
8517          * @event preparedata
8518          * Fires on every row to render, to allow you to change the data.
8519          * @param {Roo.View} this
8520          * @param {Object} data to be rendered (change this)
8521          */
8522           "preparedata" : true
8523           
8524           
8525         });
8526
8527
8528
8529     this.el.on({
8530         "click": this.onClick,
8531         "dblclick": this.onDblClick,
8532         "contextmenu": this.onContextMenu,
8533         scope:this
8534     });
8535
8536     this.selections = [];
8537     this.nodes = [];
8538     this.cmp = new Roo.CompositeElementLite([]);
8539     if(this.store){
8540         this.store = Roo.factory(this.store, Roo.data);
8541         this.setStore(this.store, true);
8542     }
8543     
8544     if ( this.footer && this.footer.xtype) {
8545            
8546          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8547         
8548         this.footer.dataSource = this.store;
8549         this.footer.container = fctr;
8550         this.footer = Roo.factory(this.footer, Roo);
8551         fctr.insertFirst(this.el);
8552         
8553         // this is a bit insane - as the paging toolbar seems to detach the el..
8554 //        dom.parentNode.parentNode.parentNode
8555          // they get detached?
8556     }
8557     
8558     
8559     Roo.View.superclass.constructor.call(this);
8560     
8561     
8562 };
8563
8564 Roo.extend(Roo.View, Roo.util.Observable, {
8565     
8566      /**
8567      * @cfg {Roo.data.Store} store Data store to load data from.
8568      */
8569     store : false,
8570     
8571     /**
8572      * @cfg {String|Roo.Element} el The container element.
8573      */
8574     el : '',
8575     
8576     /**
8577      * @cfg {String|Roo.Template} tpl The template used by this View 
8578      */
8579     tpl : false,
8580     /**
8581      * @cfg {String} dataName the named area of the template to use as the data area
8582      *                          Works with domtemplates roo-name="name"
8583      */
8584     dataName: false,
8585     /**
8586      * @cfg {String} selectedClass The css class to add to selected nodes
8587      */
8588     selectedClass : "x-view-selected",
8589      /**
8590      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8591      */
8592     emptyText : "",
8593     
8594     /**
8595      * @cfg {String} text to display on mask (default Loading)
8596      */
8597     mask : false,
8598     /**
8599      * @cfg {Boolean} multiSelect Allow multiple selection
8600      */
8601     multiSelect : false,
8602     /**
8603      * @cfg {Boolean} singleSelect Allow single selection
8604      */
8605     singleSelect:  false,
8606     
8607     /**
8608      * @cfg {Boolean} toggleSelect - selecting 
8609      */
8610     toggleSelect : false,
8611     
8612     /**
8613      * @cfg {Boolean} tickable - selecting 
8614      */
8615     tickable : false,
8616     
8617     /**
8618      * Returns the element this view is bound to.
8619      * @return {Roo.Element}
8620      */
8621     getEl : function(){
8622         return this.wrapEl;
8623     },
8624     
8625     
8626
8627     /**
8628      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8629      */
8630     refresh : function(){
8631         //Roo.log('refresh');
8632         var t = this.tpl;
8633         
8634         // if we are using something like 'domtemplate', then
8635         // the what gets used is:
8636         // t.applySubtemplate(NAME, data, wrapping data..)
8637         // the outer template then get' applied with
8638         //     the store 'extra data'
8639         // and the body get's added to the
8640         //      roo-name="data" node?
8641         //      <span class='roo-tpl-{name}'></span> ?????
8642         
8643         
8644         
8645         this.clearSelections();
8646         this.el.update("");
8647         var html = [];
8648         var records = this.store.getRange();
8649         if(records.length < 1) {
8650             
8651             // is this valid??  = should it render a template??
8652             
8653             this.el.update(this.emptyText);
8654             return;
8655         }
8656         var el = this.el;
8657         if (this.dataName) {
8658             this.el.update(t.apply(this.store.meta)); //????
8659             el = this.el.child('.roo-tpl-' + this.dataName);
8660         }
8661         
8662         for(var i = 0, len = records.length; i < len; i++){
8663             var data = this.prepareData(records[i].data, i, records[i]);
8664             this.fireEvent("preparedata", this, data, i, records[i]);
8665             
8666             var d = Roo.apply({}, data);
8667             
8668             if(this.tickable){
8669                 Roo.apply(d, {'roo-id' : Roo.id()});
8670                 
8671                 var _this = this;
8672             
8673                 Roo.each(this.parent.item, function(item){
8674                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8675                         return;
8676                     }
8677                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8678                 });
8679             }
8680             
8681             html[html.length] = Roo.util.Format.trim(
8682                 this.dataName ?
8683                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8684                     t.apply(d)
8685             );
8686         }
8687         
8688         
8689         
8690         el.update(html.join(""));
8691         this.nodes = el.dom.childNodes;
8692         this.updateIndexes(0);
8693     },
8694     
8695
8696     /**
8697      * Function to override to reformat the data that is sent to
8698      * the template for each node.
8699      * DEPRICATED - use the preparedata event handler.
8700      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8701      * a JSON object for an UpdateManager bound view).
8702      */
8703     prepareData : function(data, index, record)
8704     {
8705         this.fireEvent("preparedata", this, data, index, record);
8706         return data;
8707     },
8708
8709     onUpdate : function(ds, record){
8710         // Roo.log('on update');   
8711         this.clearSelections();
8712         var index = this.store.indexOf(record);
8713         var n = this.nodes[index];
8714         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8715         n.parentNode.removeChild(n);
8716         this.updateIndexes(index, index);
8717     },
8718
8719     
8720     
8721 // --------- FIXME     
8722     onAdd : function(ds, records, index)
8723     {
8724         //Roo.log(['on Add', ds, records, index] );        
8725         this.clearSelections();
8726         if(this.nodes.length == 0){
8727             this.refresh();
8728             return;
8729         }
8730         var n = this.nodes[index];
8731         for(var i = 0, len = records.length; i < len; i++){
8732             var d = this.prepareData(records[i].data, i, records[i]);
8733             if(n){
8734                 this.tpl.insertBefore(n, d);
8735             }else{
8736                 
8737                 this.tpl.append(this.el, d);
8738             }
8739         }
8740         this.updateIndexes(index);
8741     },
8742
8743     onRemove : function(ds, record, index){
8744        // Roo.log('onRemove');
8745         this.clearSelections();
8746         var el = this.dataName  ?
8747             this.el.child('.roo-tpl-' + this.dataName) :
8748             this.el; 
8749         
8750         el.dom.removeChild(this.nodes[index]);
8751         this.updateIndexes(index);
8752     },
8753
8754     /**
8755      * Refresh an individual node.
8756      * @param {Number} index
8757      */
8758     refreshNode : function(index){
8759         this.onUpdate(this.store, this.store.getAt(index));
8760     },
8761
8762     updateIndexes : function(startIndex, endIndex){
8763         var ns = this.nodes;
8764         startIndex = startIndex || 0;
8765         endIndex = endIndex || ns.length - 1;
8766         for(var i = startIndex; i <= endIndex; i++){
8767             ns[i].nodeIndex = i;
8768         }
8769     },
8770
8771     /**
8772      * Changes the data store this view uses and refresh the view.
8773      * @param {Store} store
8774      */
8775     setStore : function(store, initial){
8776         if(!initial && this.store){
8777             this.store.un("datachanged", this.refresh);
8778             this.store.un("add", this.onAdd);
8779             this.store.un("remove", this.onRemove);
8780             this.store.un("update", this.onUpdate);
8781             this.store.un("clear", this.refresh);
8782             this.store.un("beforeload", this.onBeforeLoad);
8783             this.store.un("load", this.onLoad);
8784             this.store.un("loadexception", this.onLoad);
8785         }
8786         if(store){
8787           
8788             store.on("datachanged", this.refresh, this);
8789             store.on("add", this.onAdd, this);
8790             store.on("remove", this.onRemove, this);
8791             store.on("update", this.onUpdate, this);
8792             store.on("clear", this.refresh, this);
8793             store.on("beforeload", this.onBeforeLoad, this);
8794             store.on("load", this.onLoad, this);
8795             store.on("loadexception", this.onLoad, this);
8796         }
8797         
8798         if(store){
8799             this.refresh();
8800         }
8801     },
8802     /**
8803      * onbeforeLoad - masks the loading area.
8804      *
8805      */
8806     onBeforeLoad : function(store,opts)
8807     {
8808          //Roo.log('onBeforeLoad');   
8809         if (!opts.add) {
8810             this.el.update("");
8811         }
8812         this.el.mask(this.mask ? this.mask : "Loading" ); 
8813     },
8814     onLoad : function ()
8815     {
8816         this.el.unmask();
8817     },
8818     
8819
8820     /**
8821      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8822      * @param {HTMLElement} node
8823      * @return {HTMLElement} The template node
8824      */
8825     findItemFromChild : function(node){
8826         var el = this.dataName  ?
8827             this.el.child('.roo-tpl-' + this.dataName,true) :
8828             this.el.dom; 
8829         
8830         if(!node || node.parentNode == el){
8831                     return node;
8832             }
8833             var p = node.parentNode;
8834             while(p && p != el){
8835             if(p.parentNode == el){
8836                 return p;
8837             }
8838             p = p.parentNode;
8839         }
8840             return null;
8841     },
8842
8843     /** @ignore */
8844     onClick : function(e){
8845         var item = this.findItemFromChild(e.getTarget());
8846         if(item){
8847             var index = this.indexOf(item);
8848             if(this.onItemClick(item, index, e) !== false){
8849                 this.fireEvent("click", this, index, item, e);
8850             }
8851         }else{
8852             this.clearSelections();
8853         }
8854     },
8855
8856     /** @ignore */
8857     onContextMenu : function(e){
8858         var item = this.findItemFromChild(e.getTarget());
8859         if(item){
8860             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8861         }
8862     },
8863
8864     /** @ignore */
8865     onDblClick : function(e){
8866         var item = this.findItemFromChild(e.getTarget());
8867         if(item){
8868             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8869         }
8870     },
8871
8872     onItemClick : function(item, index, e)
8873     {
8874         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8875             return false;
8876         }
8877         if (this.toggleSelect) {
8878             var m = this.isSelected(item) ? 'unselect' : 'select';
8879             //Roo.log(m);
8880             var _t = this;
8881             _t[m](item, true, false);
8882             return true;
8883         }
8884         if(this.multiSelect || this.singleSelect){
8885             if(this.multiSelect && e.shiftKey && this.lastSelection){
8886                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8887             }else{
8888                 this.select(item, this.multiSelect && e.ctrlKey);
8889                 this.lastSelection = item;
8890             }
8891             
8892             if(!this.tickable){
8893                 e.preventDefault();
8894             }
8895             
8896         }
8897         return true;
8898     },
8899
8900     /**
8901      * Get the number of selected nodes.
8902      * @return {Number}
8903      */
8904     getSelectionCount : function(){
8905         return this.selections.length;
8906     },
8907
8908     /**
8909      * Get the currently selected nodes.
8910      * @return {Array} An array of HTMLElements
8911      */
8912     getSelectedNodes : function(){
8913         return this.selections;
8914     },
8915
8916     /**
8917      * Get the indexes of the selected nodes.
8918      * @return {Array}
8919      */
8920     getSelectedIndexes : function(){
8921         var indexes = [], s = this.selections;
8922         for(var i = 0, len = s.length; i < len; i++){
8923             indexes.push(s[i].nodeIndex);
8924         }
8925         return indexes;
8926     },
8927
8928     /**
8929      * Clear all selections
8930      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8931      */
8932     clearSelections : function(suppressEvent){
8933         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8934             this.cmp.elements = this.selections;
8935             this.cmp.removeClass(this.selectedClass);
8936             this.selections = [];
8937             if(!suppressEvent){
8938                 this.fireEvent("selectionchange", this, this.selections);
8939             }
8940         }
8941     },
8942
8943     /**
8944      * Returns true if the passed node is selected
8945      * @param {HTMLElement/Number} node The node or node index
8946      * @return {Boolean}
8947      */
8948     isSelected : function(node){
8949         var s = this.selections;
8950         if(s.length < 1){
8951             return false;
8952         }
8953         node = this.getNode(node);
8954         return s.indexOf(node) !== -1;
8955     },
8956
8957     /**
8958      * Selects nodes.
8959      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8960      * @param {Boolean} keepExisting (optional) true to keep existing selections
8961      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8962      */
8963     select : function(nodeInfo, keepExisting, suppressEvent){
8964         if(nodeInfo instanceof Array){
8965             if(!keepExisting){
8966                 this.clearSelections(true);
8967             }
8968             for(var i = 0, len = nodeInfo.length; i < len; i++){
8969                 this.select(nodeInfo[i], true, true);
8970             }
8971             return;
8972         } 
8973         var node = this.getNode(nodeInfo);
8974         if(!node || this.isSelected(node)){
8975             return; // already selected.
8976         }
8977         if(!keepExisting){
8978             this.clearSelections(true);
8979         }
8980         
8981         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8982             Roo.fly(node).addClass(this.selectedClass);
8983             this.selections.push(node);
8984             if(!suppressEvent){
8985                 this.fireEvent("selectionchange", this, this.selections);
8986             }
8987         }
8988         
8989         
8990     },
8991       /**
8992      * Unselects nodes.
8993      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8994      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8995      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8996      */
8997     unselect : function(nodeInfo, keepExisting, suppressEvent)
8998     {
8999         if(nodeInfo instanceof Array){
9000             Roo.each(this.selections, function(s) {
9001                 this.unselect(s, nodeInfo);
9002             }, this);
9003             return;
9004         }
9005         var node = this.getNode(nodeInfo);
9006         if(!node || !this.isSelected(node)){
9007             //Roo.log("not selected");
9008             return; // not selected.
9009         }
9010         // fireevent???
9011         var ns = [];
9012         Roo.each(this.selections, function(s) {
9013             if (s == node ) {
9014                 Roo.fly(node).removeClass(this.selectedClass);
9015
9016                 return;
9017             }
9018             ns.push(s);
9019         },this);
9020         
9021         this.selections= ns;
9022         this.fireEvent("selectionchange", this, this.selections);
9023     },
9024
9025     /**
9026      * Gets a template node.
9027      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9028      * @return {HTMLElement} The node or null if it wasn't found
9029      */
9030     getNode : function(nodeInfo){
9031         if(typeof nodeInfo == "string"){
9032             return document.getElementById(nodeInfo);
9033         }else if(typeof nodeInfo == "number"){
9034             return this.nodes[nodeInfo];
9035         }
9036         return nodeInfo;
9037     },
9038
9039     /**
9040      * Gets a range template nodes.
9041      * @param {Number} startIndex
9042      * @param {Number} endIndex
9043      * @return {Array} An array of nodes
9044      */
9045     getNodes : function(start, end){
9046         var ns = this.nodes;
9047         start = start || 0;
9048         end = typeof end == "undefined" ? ns.length - 1 : end;
9049         var nodes = [];
9050         if(start <= end){
9051             for(var i = start; i <= end; i++){
9052                 nodes.push(ns[i]);
9053             }
9054         } else{
9055             for(var i = start; i >= end; i--){
9056                 nodes.push(ns[i]);
9057             }
9058         }
9059         return nodes;
9060     },
9061
9062     /**
9063      * Finds the index of the passed node
9064      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9065      * @return {Number} The index of the node or -1
9066      */
9067     indexOf : function(node){
9068         node = this.getNode(node);
9069         if(typeof node.nodeIndex == "number"){
9070             return node.nodeIndex;
9071         }
9072         var ns = this.nodes;
9073         for(var i = 0, len = ns.length; i < len; i++){
9074             if(ns[i] == node){
9075                 return i;
9076             }
9077         }
9078         return -1;
9079     }
9080 });
9081 /*
9082  * Based on:
9083  * Ext JS Library 1.1.1
9084  * Copyright(c) 2006-2007, Ext JS, LLC.
9085  *
9086  * Originally Released Under LGPL - original licence link has changed is not relivant.
9087  *
9088  * Fork - LGPL
9089  * <script type="text/javascript">
9090  */
9091
9092 /**
9093  * @class Roo.JsonView
9094  * @extends Roo.View
9095  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9096 <pre><code>
9097 var view = new Roo.JsonView({
9098     container: "my-element",
9099     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9100     multiSelect: true, 
9101     jsonRoot: "data" 
9102 });
9103
9104 // listen for node click?
9105 view.on("click", function(vw, index, node, e){
9106     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9107 });
9108
9109 // direct load of JSON data
9110 view.load("foobar.php");
9111
9112 // Example from my blog list
9113 var tpl = new Roo.Template(
9114     '&lt;div class="entry"&gt;' +
9115     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9116     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9117     "&lt;/div&gt;&lt;hr /&gt;"
9118 );
9119
9120 var moreView = new Roo.JsonView({
9121     container :  "entry-list", 
9122     template : tpl,
9123     jsonRoot: "posts"
9124 });
9125 moreView.on("beforerender", this.sortEntries, this);
9126 moreView.load({
9127     url: "/blog/get-posts.php",
9128     params: "allposts=true",
9129     text: "Loading Blog Entries..."
9130 });
9131 </code></pre>
9132
9133 * Note: old code is supported with arguments : (container, template, config)
9134
9135
9136  * @constructor
9137  * Create a new JsonView
9138  * 
9139  * @param {Object} config The config object
9140  * 
9141  */
9142 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9143     
9144     
9145     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9146
9147     var um = this.el.getUpdateManager();
9148     um.setRenderer(this);
9149     um.on("update", this.onLoad, this);
9150     um.on("failure", this.onLoadException, this);
9151
9152     /**
9153      * @event beforerender
9154      * Fires before rendering of the downloaded JSON data.
9155      * @param {Roo.JsonView} this
9156      * @param {Object} data The JSON data loaded
9157      */
9158     /**
9159      * @event load
9160      * Fires when data is loaded.
9161      * @param {Roo.JsonView} this
9162      * @param {Object} data The JSON data loaded
9163      * @param {Object} response The raw Connect response object
9164      */
9165     /**
9166      * @event loadexception
9167      * Fires when loading fails.
9168      * @param {Roo.JsonView} this
9169      * @param {Object} response The raw Connect response object
9170      */
9171     this.addEvents({
9172         'beforerender' : true,
9173         'load' : true,
9174         'loadexception' : true
9175     });
9176 };
9177 Roo.extend(Roo.JsonView, Roo.View, {
9178     /**
9179      * @type {String} The root property in the loaded JSON object that contains the data
9180      */
9181     jsonRoot : "",
9182
9183     /**
9184      * Refreshes the view.
9185      */
9186     refresh : function(){
9187         this.clearSelections();
9188         this.el.update("");
9189         var html = [];
9190         var o = this.jsonData;
9191         if(o && o.length > 0){
9192             for(var i = 0, len = o.length; i < len; i++){
9193                 var data = this.prepareData(o[i], i, o);
9194                 html[html.length] = this.tpl.apply(data);
9195             }
9196         }else{
9197             html.push(this.emptyText);
9198         }
9199         this.el.update(html.join(""));
9200         this.nodes = this.el.dom.childNodes;
9201         this.updateIndexes(0);
9202     },
9203
9204     /**
9205      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9206      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9207      <pre><code>
9208      view.load({
9209          url: "your-url.php",
9210          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9211          callback: yourFunction,
9212          scope: yourObject, //(optional scope)
9213          discardUrl: false,
9214          nocache: false,
9215          text: "Loading...",
9216          timeout: 30,
9217          scripts: false
9218      });
9219      </code></pre>
9220      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9221      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9222      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9223      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9224      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9225      */
9226     load : function(){
9227         var um = this.el.getUpdateManager();
9228         um.update.apply(um, arguments);
9229     },
9230
9231     // note - render is a standard framework call...
9232     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
9233     render : function(el, response){
9234         
9235         this.clearSelections();
9236         this.el.update("");
9237         var o;
9238         try{
9239             if (response != '') {
9240                 o = Roo.util.JSON.decode(response.responseText);
9241                 if(this.jsonRoot){
9242                     
9243                     o = o[this.jsonRoot];
9244                 }
9245             }
9246         } catch(e){
9247         }
9248         /**
9249          * The current JSON data or null
9250          */
9251         this.jsonData = o;
9252         this.beforeRender();
9253         this.refresh();
9254     },
9255
9256 /**
9257  * Get the number of records in the current JSON dataset
9258  * @return {Number}
9259  */
9260     getCount : function(){
9261         return this.jsonData ? this.jsonData.length : 0;
9262     },
9263
9264 /**
9265  * Returns the JSON object for the specified node(s)
9266  * @param {HTMLElement/Array} node The node or an array of nodes
9267  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9268  * you get the JSON object for the node
9269  */
9270     getNodeData : function(node){
9271         if(node instanceof Array){
9272             var data = [];
9273             for(var i = 0, len = node.length; i < len; i++){
9274                 data.push(this.getNodeData(node[i]));
9275             }
9276             return data;
9277         }
9278         return this.jsonData[this.indexOf(node)] || null;
9279     },
9280
9281     beforeRender : function(){
9282         this.snapshot = this.jsonData;
9283         if(this.sortInfo){
9284             this.sort.apply(this, this.sortInfo);
9285         }
9286         this.fireEvent("beforerender", this, this.jsonData);
9287     },
9288
9289     onLoad : function(el, o){
9290         this.fireEvent("load", this, this.jsonData, o);
9291     },
9292
9293     onLoadException : function(el, o){
9294         this.fireEvent("loadexception", this, o);
9295     },
9296
9297 /**
9298  * Filter the data by a specific property.
9299  * @param {String} property A property on your JSON objects
9300  * @param {String/RegExp} value Either string that the property values
9301  * should start with, or a RegExp to test against the property
9302  */
9303     filter : function(property, value){
9304         if(this.jsonData){
9305             var data = [];
9306             var ss = this.snapshot;
9307             if(typeof value == "string"){
9308                 var vlen = value.length;
9309                 if(vlen == 0){
9310                     this.clearFilter();
9311                     return;
9312                 }
9313                 value = value.toLowerCase();
9314                 for(var i = 0, len = ss.length; i < len; i++){
9315                     var o = ss[i];
9316                     if(o[property].substr(0, vlen).toLowerCase() == value){
9317                         data.push(o);
9318                     }
9319                 }
9320             } else if(value.exec){ // regex?
9321                 for(var i = 0, len = ss.length; i < len; i++){
9322                     var o = ss[i];
9323                     if(value.test(o[property])){
9324                         data.push(o);
9325                     }
9326                 }
9327             } else{
9328                 return;
9329             }
9330             this.jsonData = data;
9331             this.refresh();
9332         }
9333     },
9334
9335 /**
9336  * Filter by a function. The passed function will be called with each
9337  * object in the current dataset. If the function returns true the value is kept,
9338  * otherwise it is filtered.
9339  * @param {Function} fn
9340  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9341  */
9342     filterBy : function(fn, scope){
9343         if(this.jsonData){
9344             var data = [];
9345             var ss = this.snapshot;
9346             for(var i = 0, len = ss.length; i < len; i++){
9347                 var o = ss[i];
9348                 if(fn.call(scope || this, o)){
9349                     data.push(o);
9350                 }
9351             }
9352             this.jsonData = data;
9353             this.refresh();
9354         }
9355     },
9356
9357 /**
9358  * Clears the current filter.
9359  */
9360     clearFilter : function(){
9361         if(this.snapshot && this.jsonData != this.snapshot){
9362             this.jsonData = this.snapshot;
9363             this.refresh();
9364         }
9365     },
9366
9367
9368 /**
9369  * Sorts the data for this view and refreshes it.
9370  * @param {String} property A property on your JSON objects to sort on
9371  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9372  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9373  */
9374     sort : function(property, dir, sortType){
9375         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9376         if(this.jsonData){
9377             var p = property;
9378             var dsc = dir && dir.toLowerCase() == "desc";
9379             var f = function(o1, o2){
9380                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9381                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9382                 ;
9383                 if(v1 < v2){
9384                     return dsc ? +1 : -1;
9385                 } else if(v1 > v2){
9386                     return dsc ? -1 : +1;
9387                 } else{
9388                     return 0;
9389                 }
9390             };
9391             this.jsonData.sort(f);
9392             this.refresh();
9393             if(this.jsonData != this.snapshot){
9394                 this.snapshot.sort(f);
9395             }
9396         }
9397     }
9398 });/*
9399  * Based on:
9400  * Ext JS Library 1.1.1
9401  * Copyright(c) 2006-2007, Ext JS, LLC.
9402  *
9403  * Originally Released Under LGPL - original licence link has changed is not relivant.
9404  *
9405  * Fork - LGPL
9406  * <script type="text/javascript">
9407  */
9408  
9409
9410 /**
9411  * @class Roo.ColorPalette
9412  * @extends Roo.Component
9413  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9414  * Here's an example of typical usage:
9415  * <pre><code>
9416 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9417 cp.render('my-div');
9418
9419 cp.on('select', function(palette, selColor){
9420     // do something with selColor
9421 });
9422 </code></pre>
9423  * @constructor
9424  * Create a new ColorPalette
9425  * @param {Object} config The config object
9426  */
9427 Roo.ColorPalette = function(config){
9428     Roo.ColorPalette.superclass.constructor.call(this, config);
9429     this.addEvents({
9430         /**
9431              * @event select
9432              * Fires when a color is selected
9433              * @param {ColorPalette} this
9434              * @param {String} color The 6-digit color hex code (without the # symbol)
9435              */
9436         select: true
9437     });
9438
9439     if(this.handler){
9440         this.on("select", this.handler, this.scope, true);
9441     }
9442 };
9443 Roo.extend(Roo.ColorPalette, Roo.Component, {
9444     /**
9445      * @cfg {String} itemCls
9446      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9447      */
9448     itemCls : "x-color-palette",
9449     /**
9450      * @cfg {String} value
9451      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9452      * the hex codes are case-sensitive.
9453      */
9454     value : null,
9455     clickEvent:'click',
9456     // private
9457     ctype: "Roo.ColorPalette",
9458
9459     /**
9460      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9461      */
9462     allowReselect : false,
9463
9464     /**
9465      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9466      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9467      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9468      * of colors with the width setting until the box is symmetrical.</p>
9469      * <p>You can override individual colors if needed:</p>
9470      * <pre><code>
9471 var cp = new Roo.ColorPalette();
9472 cp.colors[0] = "FF0000";  // change the first box to red
9473 </code></pre>
9474
9475 Or you can provide a custom array of your own for complete control:
9476 <pre><code>
9477 var cp = new Roo.ColorPalette();
9478 cp.colors = ["000000", "993300", "333300"];
9479 </code></pre>
9480      * @type Array
9481      */
9482     colors : [
9483         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9484         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9485         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9486         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9487         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9488     ],
9489
9490     // private
9491     onRender : function(container, position){
9492         var t = new Roo.MasterTemplate(
9493             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9494         );
9495         var c = this.colors;
9496         for(var i = 0, len = c.length; i < len; i++){
9497             t.add([c[i]]);
9498         }
9499         var el = document.createElement("div");
9500         el.className = this.itemCls;
9501         t.overwrite(el);
9502         container.dom.insertBefore(el, position);
9503         this.el = Roo.get(el);
9504         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9505         if(this.clickEvent != 'click'){
9506             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9507         }
9508     },
9509
9510     // private
9511     afterRender : function(){
9512         Roo.ColorPalette.superclass.afterRender.call(this);
9513         if(this.value){
9514             var s = this.value;
9515             this.value = null;
9516             this.select(s);
9517         }
9518     },
9519
9520     // private
9521     handleClick : function(e, t){
9522         e.preventDefault();
9523         if(!this.disabled){
9524             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9525             this.select(c.toUpperCase());
9526         }
9527     },
9528
9529     /**
9530      * Selects the specified color in the palette (fires the select event)
9531      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9532      */
9533     select : function(color){
9534         color = color.replace("#", "");
9535         if(color != this.value || this.allowReselect){
9536             var el = this.el;
9537             if(this.value){
9538                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9539             }
9540             el.child("a.color-"+color).addClass("x-color-palette-sel");
9541             this.value = color;
9542             this.fireEvent("select", this, color);
9543         }
9544     }
9545 });/*
9546  * Based on:
9547  * Ext JS Library 1.1.1
9548  * Copyright(c) 2006-2007, Ext JS, LLC.
9549  *
9550  * Originally Released Under LGPL - original licence link has changed is not relivant.
9551  *
9552  * Fork - LGPL
9553  * <script type="text/javascript">
9554  */
9555  
9556 /**
9557  * @class Roo.DatePicker
9558  * @extends Roo.Component
9559  * Simple date picker class.
9560  * @constructor
9561  * Create a new DatePicker
9562  * @param {Object} config The config object
9563  */
9564 Roo.DatePicker = function(config){
9565     Roo.DatePicker.superclass.constructor.call(this, config);
9566
9567     this.value = config && config.value ?
9568                  config.value.clearTime() : new Date().clearTime();
9569
9570     this.addEvents({
9571         /**
9572              * @event select
9573              * Fires when a date is selected
9574              * @param {DatePicker} this
9575              * @param {Date} date The selected date
9576              */
9577         'select': true,
9578         /**
9579              * @event monthchange
9580              * Fires when the displayed month changes 
9581              * @param {DatePicker} this
9582              * @param {Date} date The selected month
9583              */
9584         'monthchange': true
9585     });
9586
9587     if(this.handler){
9588         this.on("select", this.handler,  this.scope || this);
9589     }
9590     // build the disabledDatesRE
9591     if(!this.disabledDatesRE && this.disabledDates){
9592         var dd = this.disabledDates;
9593         var re = "(?:";
9594         for(var i = 0; i < dd.length; i++){
9595             re += dd[i];
9596             if(i != dd.length-1) {
9597                 re += "|";
9598             }
9599         }
9600         this.disabledDatesRE = new RegExp(re + ")");
9601     }
9602 };
9603
9604 Roo.extend(Roo.DatePicker, Roo.Component, {
9605     /**
9606      * @cfg {String} todayText
9607      * The text to display on the button that selects the current date (defaults to "Today")
9608      */
9609     todayText : "Today",
9610     /**
9611      * @cfg {String} okText
9612      * The text to display on the ok button
9613      */
9614     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9615     /**
9616      * @cfg {String} cancelText
9617      * The text to display on the cancel button
9618      */
9619     cancelText : "Cancel",
9620     /**
9621      * @cfg {String} todayTip
9622      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9623      */
9624     todayTip : "{0} (Spacebar)",
9625     /**
9626      * @cfg {Date} minDate
9627      * Minimum allowable date (JavaScript date object, defaults to null)
9628      */
9629     minDate : null,
9630     /**
9631      * @cfg {Date} maxDate
9632      * Maximum allowable date (JavaScript date object, defaults to null)
9633      */
9634     maxDate : null,
9635     /**
9636      * @cfg {String} minText
9637      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9638      */
9639     minText : "This date is before the minimum date",
9640     /**
9641      * @cfg {String} maxText
9642      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9643      */
9644     maxText : "This date is after the maximum date",
9645     /**
9646      * @cfg {String} format
9647      * The default date format string which can be overriden for localization support.  The format must be
9648      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9649      */
9650     format : "m/d/y",
9651     /**
9652      * @cfg {Array} disabledDays
9653      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9654      */
9655     disabledDays : null,
9656     /**
9657      * @cfg {String} disabledDaysText
9658      * The tooltip to display when the date falls on a disabled day (defaults to "")
9659      */
9660     disabledDaysText : "",
9661     /**
9662      * @cfg {RegExp} disabledDatesRE
9663      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9664      */
9665     disabledDatesRE : null,
9666     /**
9667      * @cfg {String} disabledDatesText
9668      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9669      */
9670     disabledDatesText : "",
9671     /**
9672      * @cfg {Boolean} constrainToViewport
9673      * True to constrain the date picker to the viewport (defaults to true)
9674      */
9675     constrainToViewport : true,
9676     /**
9677      * @cfg {Array} monthNames
9678      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9679      */
9680     monthNames : Date.monthNames,
9681     /**
9682      * @cfg {Array} dayNames
9683      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9684      */
9685     dayNames : Date.dayNames,
9686     /**
9687      * @cfg {String} nextText
9688      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9689      */
9690     nextText: 'Next Month (Control+Right)',
9691     /**
9692      * @cfg {String} prevText
9693      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9694      */
9695     prevText: 'Previous Month (Control+Left)',
9696     /**
9697      * @cfg {String} monthYearText
9698      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9699      */
9700     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9701     /**
9702      * @cfg {Number} startDay
9703      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9704      */
9705     startDay : 0,
9706     /**
9707      * @cfg {Bool} showClear
9708      * Show a clear button (usefull for date form elements that can be blank.)
9709      */
9710     
9711     showClear: false,
9712     
9713     /**
9714      * Sets the value of the date field
9715      * @param {Date} value The date to set
9716      */
9717     setValue : function(value){
9718         var old = this.value;
9719         
9720         if (typeof(value) == 'string') {
9721          
9722             value = Date.parseDate(value, this.format);
9723         }
9724         if (!value) {
9725             value = new Date();
9726         }
9727         
9728         this.value = value.clearTime(true);
9729         if(this.el){
9730             this.update(this.value);
9731         }
9732     },
9733
9734     /**
9735      * Gets the current selected value of the date field
9736      * @return {Date} The selected date
9737      */
9738     getValue : function(){
9739         return this.value;
9740     },
9741
9742     // private
9743     focus : function(){
9744         if(this.el){
9745             this.update(this.activeDate);
9746         }
9747     },
9748
9749     // privateval
9750     onRender : function(container, position){
9751         
9752         var m = [
9753              '<table cellspacing="0">',
9754                 '<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>',
9755                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9756         var dn = this.dayNames;
9757         for(var i = 0; i < 7; i++){
9758             var d = this.startDay+i;
9759             if(d > 6){
9760                 d = d-7;
9761             }
9762             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9763         }
9764         m[m.length] = "</tr></thead><tbody><tr>";
9765         for(var i = 0; i < 42; i++) {
9766             if(i % 7 == 0 && i != 0){
9767                 m[m.length] = "</tr><tr>";
9768             }
9769             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9770         }
9771         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9772             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9773
9774         var el = document.createElement("div");
9775         el.className = "x-date-picker";
9776         el.innerHTML = m.join("");
9777
9778         container.dom.insertBefore(el, position);
9779
9780         this.el = Roo.get(el);
9781         this.eventEl = Roo.get(el.firstChild);
9782
9783         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9784             handler: this.showPrevMonth,
9785             scope: this,
9786             preventDefault:true,
9787             stopDefault:true
9788         });
9789
9790         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9791             handler: this.showNextMonth,
9792             scope: this,
9793             preventDefault:true,
9794             stopDefault:true
9795         });
9796
9797         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9798
9799         this.monthPicker = this.el.down('div.x-date-mp');
9800         this.monthPicker.enableDisplayMode('block');
9801         
9802         var kn = new Roo.KeyNav(this.eventEl, {
9803             "left" : function(e){
9804                 e.ctrlKey ?
9805                     this.showPrevMonth() :
9806                     this.update(this.activeDate.add("d", -1));
9807             },
9808
9809             "right" : function(e){
9810                 e.ctrlKey ?
9811                     this.showNextMonth() :
9812                     this.update(this.activeDate.add("d", 1));
9813             },
9814
9815             "up" : function(e){
9816                 e.ctrlKey ?
9817                     this.showNextYear() :
9818                     this.update(this.activeDate.add("d", -7));
9819             },
9820
9821             "down" : function(e){
9822                 e.ctrlKey ?
9823                     this.showPrevYear() :
9824                     this.update(this.activeDate.add("d", 7));
9825             },
9826
9827             "pageUp" : function(e){
9828                 this.showNextMonth();
9829             },
9830
9831             "pageDown" : function(e){
9832                 this.showPrevMonth();
9833             },
9834
9835             "enter" : function(e){
9836                 e.stopPropagation();
9837                 return true;
9838             },
9839
9840             scope : this
9841         });
9842
9843         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9844
9845         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9846
9847         this.el.unselectable();
9848         
9849         this.cells = this.el.select("table.x-date-inner tbody td");
9850         this.textNodes = this.el.query("table.x-date-inner tbody span");
9851
9852         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9853             text: "&#160;",
9854             tooltip: this.monthYearText
9855         });
9856
9857         this.mbtn.on('click', this.showMonthPicker, this);
9858         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9859
9860
9861         var today = (new Date()).dateFormat(this.format);
9862         
9863         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9864         if (this.showClear) {
9865             baseTb.add( new Roo.Toolbar.Fill());
9866         }
9867         baseTb.add({
9868             text: String.format(this.todayText, today),
9869             tooltip: String.format(this.todayTip, today),
9870             handler: this.selectToday,
9871             scope: this
9872         });
9873         
9874         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9875             
9876         //});
9877         if (this.showClear) {
9878             
9879             baseTb.add( new Roo.Toolbar.Fill());
9880             baseTb.add({
9881                 text: '&#160;',
9882                 cls: 'x-btn-icon x-btn-clear',
9883                 handler: function() {
9884                     //this.value = '';
9885                     this.fireEvent("select", this, '');
9886                 },
9887                 scope: this
9888             });
9889         }
9890         
9891         
9892         if(Roo.isIE){
9893             this.el.repaint();
9894         }
9895         this.update(this.value);
9896     },
9897
9898     createMonthPicker : function(){
9899         if(!this.monthPicker.dom.firstChild){
9900             var buf = ['<table border="0" cellspacing="0">'];
9901             for(var i = 0; i < 6; i++){
9902                 buf.push(
9903                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9904                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9905                     i == 0 ?
9906                     '<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>' :
9907                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9908                 );
9909             }
9910             buf.push(
9911                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9912                     this.okText,
9913                     '</button><button type="button" class="x-date-mp-cancel">',
9914                     this.cancelText,
9915                     '</button></td></tr>',
9916                 '</table>'
9917             );
9918             this.monthPicker.update(buf.join(''));
9919             this.monthPicker.on('click', this.onMonthClick, this);
9920             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9921
9922             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9923             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9924
9925             this.mpMonths.each(function(m, a, i){
9926                 i += 1;
9927                 if((i%2) == 0){
9928                     m.dom.xmonth = 5 + Math.round(i * .5);
9929                 }else{
9930                     m.dom.xmonth = Math.round((i-1) * .5);
9931                 }
9932             });
9933         }
9934     },
9935
9936     showMonthPicker : function(){
9937         this.createMonthPicker();
9938         var size = this.el.getSize();
9939         this.monthPicker.setSize(size);
9940         this.monthPicker.child('table').setSize(size);
9941
9942         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9943         this.updateMPMonth(this.mpSelMonth);
9944         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9945         this.updateMPYear(this.mpSelYear);
9946
9947         this.monthPicker.slideIn('t', {duration:.2});
9948     },
9949
9950     updateMPYear : function(y){
9951         this.mpyear = y;
9952         var ys = this.mpYears.elements;
9953         for(var i = 1; i <= 10; i++){
9954             var td = ys[i-1], y2;
9955             if((i%2) == 0){
9956                 y2 = y + Math.round(i * .5);
9957                 td.firstChild.innerHTML = y2;
9958                 td.xyear = y2;
9959             }else{
9960                 y2 = y - (5-Math.round(i * .5));
9961                 td.firstChild.innerHTML = y2;
9962                 td.xyear = y2;
9963             }
9964             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9965         }
9966     },
9967
9968     updateMPMonth : function(sm){
9969         this.mpMonths.each(function(m, a, i){
9970             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9971         });
9972     },
9973
9974     selectMPMonth: function(m){
9975         
9976     },
9977
9978     onMonthClick : function(e, t){
9979         e.stopEvent();
9980         var el = new Roo.Element(t), pn;
9981         if(el.is('button.x-date-mp-cancel')){
9982             this.hideMonthPicker();
9983         }
9984         else if(el.is('button.x-date-mp-ok')){
9985             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9986             this.hideMonthPicker();
9987         }
9988         else if(pn = el.up('td.x-date-mp-month', 2)){
9989             this.mpMonths.removeClass('x-date-mp-sel');
9990             pn.addClass('x-date-mp-sel');
9991             this.mpSelMonth = pn.dom.xmonth;
9992         }
9993         else if(pn = el.up('td.x-date-mp-year', 2)){
9994             this.mpYears.removeClass('x-date-mp-sel');
9995             pn.addClass('x-date-mp-sel');
9996             this.mpSelYear = pn.dom.xyear;
9997         }
9998         else if(el.is('a.x-date-mp-prev')){
9999             this.updateMPYear(this.mpyear-10);
10000         }
10001         else if(el.is('a.x-date-mp-next')){
10002             this.updateMPYear(this.mpyear+10);
10003         }
10004     },
10005
10006     onMonthDblClick : function(e, t){
10007         e.stopEvent();
10008         var el = new Roo.Element(t), pn;
10009         if(pn = el.up('td.x-date-mp-month', 2)){
10010             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10011             this.hideMonthPicker();
10012         }
10013         else if(pn = el.up('td.x-date-mp-year', 2)){
10014             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10015             this.hideMonthPicker();
10016         }
10017     },
10018
10019     hideMonthPicker : function(disableAnim){
10020         if(this.monthPicker){
10021             if(disableAnim === true){
10022                 this.monthPicker.hide();
10023             }else{
10024                 this.monthPicker.slideOut('t', {duration:.2});
10025             }
10026         }
10027     },
10028
10029     // private
10030     showPrevMonth : function(e){
10031         this.update(this.activeDate.add("mo", -1));
10032     },
10033
10034     // private
10035     showNextMonth : function(e){
10036         this.update(this.activeDate.add("mo", 1));
10037     },
10038
10039     // private
10040     showPrevYear : function(){
10041         this.update(this.activeDate.add("y", -1));
10042     },
10043
10044     // private
10045     showNextYear : function(){
10046         this.update(this.activeDate.add("y", 1));
10047     },
10048
10049     // private
10050     handleMouseWheel : function(e){
10051         var delta = e.getWheelDelta();
10052         if(delta > 0){
10053             this.showPrevMonth();
10054             e.stopEvent();
10055         } else if(delta < 0){
10056             this.showNextMonth();
10057             e.stopEvent();
10058         }
10059     },
10060
10061     // private
10062     handleDateClick : function(e, t){
10063         e.stopEvent();
10064         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10065             this.setValue(new Date(t.dateValue));
10066             this.fireEvent("select", this, this.value);
10067         }
10068     },
10069
10070     // private
10071     selectToday : function(){
10072         this.setValue(new Date().clearTime());
10073         this.fireEvent("select", this, this.value);
10074     },
10075
10076     // private
10077     update : function(date)
10078     {
10079         var vd = this.activeDate;
10080         this.activeDate = date;
10081         if(vd && this.el){
10082             var t = date.getTime();
10083             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10084                 this.cells.removeClass("x-date-selected");
10085                 this.cells.each(function(c){
10086                    if(c.dom.firstChild.dateValue == t){
10087                        c.addClass("x-date-selected");
10088                        setTimeout(function(){
10089                             try{c.dom.firstChild.focus();}catch(e){}
10090                        }, 50);
10091                        return false;
10092                    }
10093                 });
10094                 return;
10095             }
10096         }
10097         
10098         var days = date.getDaysInMonth();
10099         var firstOfMonth = date.getFirstDateOfMonth();
10100         var startingPos = firstOfMonth.getDay()-this.startDay;
10101
10102         if(startingPos <= this.startDay){
10103             startingPos += 7;
10104         }
10105
10106         var pm = date.add("mo", -1);
10107         var prevStart = pm.getDaysInMonth()-startingPos;
10108
10109         var cells = this.cells.elements;
10110         var textEls = this.textNodes;
10111         days += startingPos;
10112
10113         // convert everything to numbers so it's fast
10114         var day = 86400000;
10115         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10116         var today = new Date().clearTime().getTime();
10117         var sel = date.clearTime().getTime();
10118         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10119         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10120         var ddMatch = this.disabledDatesRE;
10121         var ddText = this.disabledDatesText;
10122         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10123         var ddaysText = this.disabledDaysText;
10124         var format = this.format;
10125
10126         var setCellClass = function(cal, cell){
10127             cell.title = "";
10128             var t = d.getTime();
10129             cell.firstChild.dateValue = t;
10130             if(t == today){
10131                 cell.className += " x-date-today";
10132                 cell.title = cal.todayText;
10133             }
10134             if(t == sel){
10135                 cell.className += " x-date-selected";
10136                 setTimeout(function(){
10137                     try{cell.firstChild.focus();}catch(e){}
10138                 }, 50);
10139             }
10140             // disabling
10141             if(t < min) {
10142                 cell.className = " x-date-disabled";
10143                 cell.title = cal.minText;
10144                 return;
10145             }
10146             if(t > max) {
10147                 cell.className = " x-date-disabled";
10148                 cell.title = cal.maxText;
10149                 return;
10150             }
10151             if(ddays){
10152                 if(ddays.indexOf(d.getDay()) != -1){
10153                     cell.title = ddaysText;
10154                     cell.className = " x-date-disabled";
10155                 }
10156             }
10157             if(ddMatch && format){
10158                 var fvalue = d.dateFormat(format);
10159                 if(ddMatch.test(fvalue)){
10160                     cell.title = ddText.replace("%0", fvalue);
10161                     cell.className = " x-date-disabled";
10162                 }
10163             }
10164         };
10165
10166         var i = 0;
10167         for(; i < startingPos; i++) {
10168             textEls[i].innerHTML = (++prevStart);
10169             d.setDate(d.getDate()+1);
10170             cells[i].className = "x-date-prevday";
10171             setCellClass(this, cells[i]);
10172         }
10173         for(; i < days; i++){
10174             intDay = i - startingPos + 1;
10175             textEls[i].innerHTML = (intDay);
10176             d.setDate(d.getDate()+1);
10177             cells[i].className = "x-date-active";
10178             setCellClass(this, cells[i]);
10179         }
10180         var extraDays = 0;
10181         for(; i < 42; i++) {
10182              textEls[i].innerHTML = (++extraDays);
10183              d.setDate(d.getDate()+1);
10184              cells[i].className = "x-date-nextday";
10185              setCellClass(this, cells[i]);
10186         }
10187
10188         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10189         this.fireEvent('monthchange', this, date);
10190         
10191         if(!this.internalRender){
10192             var main = this.el.dom.firstChild;
10193             var w = main.offsetWidth;
10194             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10195             Roo.fly(main).setWidth(w);
10196             this.internalRender = true;
10197             // opera does not respect the auto grow header center column
10198             // then, after it gets a width opera refuses to recalculate
10199             // without a second pass
10200             if(Roo.isOpera && !this.secondPass){
10201                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10202                 this.secondPass = true;
10203                 this.update.defer(10, this, [date]);
10204             }
10205         }
10206         
10207         
10208     }
10209 });        /*
10210  * Based on:
10211  * Ext JS Library 1.1.1
10212  * Copyright(c) 2006-2007, Ext JS, LLC.
10213  *
10214  * Originally Released Under LGPL - original licence link has changed is not relivant.
10215  *
10216  * Fork - LGPL
10217  * <script type="text/javascript">
10218  */
10219 /**
10220  * @class Roo.TabPanel
10221  * @extends Roo.util.Observable
10222  * A lightweight tab container.
10223  * <br><br>
10224  * Usage:
10225  * <pre><code>
10226 // basic tabs 1, built from existing content
10227 var tabs = new Roo.TabPanel("tabs1");
10228 tabs.addTab("script", "View Script");
10229 tabs.addTab("markup", "View Markup");
10230 tabs.activate("script");
10231
10232 // more advanced tabs, built from javascript
10233 var jtabs = new Roo.TabPanel("jtabs");
10234 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10235
10236 // set up the UpdateManager
10237 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10238 var updater = tab2.getUpdateManager();
10239 updater.setDefaultUrl("ajax1.htm");
10240 tab2.on('activate', updater.refresh, updater, true);
10241
10242 // Use setUrl for Ajax loading
10243 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10244 tab3.setUrl("ajax2.htm", null, true);
10245
10246 // Disabled tab
10247 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10248 tab4.disable();
10249
10250 jtabs.activate("jtabs-1");
10251  * </code></pre>
10252  * @constructor
10253  * Create a new TabPanel.
10254  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10255  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10256  */
10257 Roo.TabPanel = function(container, config){
10258     /**
10259     * The container element for this TabPanel.
10260     * @type Roo.Element
10261     */
10262     this.el = Roo.get(container, true);
10263     if(config){
10264         if(typeof config == "boolean"){
10265             this.tabPosition = config ? "bottom" : "top";
10266         }else{
10267             Roo.apply(this, config);
10268         }
10269     }
10270     if(this.tabPosition == "bottom"){
10271         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10272         this.el.addClass("x-tabs-bottom");
10273     }
10274     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10275     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10276     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10277     if(Roo.isIE){
10278         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10279     }
10280     if(this.tabPosition != "bottom"){
10281         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10282          * @type Roo.Element
10283          */
10284         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10285         this.el.addClass("x-tabs-top");
10286     }
10287     this.items = [];
10288
10289     this.bodyEl.setStyle("position", "relative");
10290
10291     this.active = null;
10292     this.activateDelegate = this.activate.createDelegate(this);
10293
10294     this.addEvents({
10295         /**
10296          * @event tabchange
10297          * Fires when the active tab changes
10298          * @param {Roo.TabPanel} this
10299          * @param {Roo.TabPanelItem} activePanel The new active tab
10300          */
10301         "tabchange": true,
10302         /**
10303          * @event beforetabchange
10304          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10305          * @param {Roo.TabPanel} this
10306          * @param {Object} e Set cancel to true on this object to cancel the tab change
10307          * @param {Roo.TabPanelItem} tab The tab being changed to
10308          */
10309         "beforetabchange" : true
10310     });
10311
10312     Roo.EventManager.onWindowResize(this.onResize, this);
10313     this.cpad = this.el.getPadding("lr");
10314     this.hiddenCount = 0;
10315
10316
10317     // toolbar on the tabbar support...
10318     if (this.toolbar) {
10319         var tcfg = this.toolbar;
10320         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10321         this.toolbar = new Roo.Toolbar(tcfg);
10322         if (Roo.isSafari) {
10323             var tbl = tcfg.container.child('table', true);
10324             tbl.setAttribute('width', '100%');
10325         }
10326         
10327     }
10328    
10329
10330
10331     Roo.TabPanel.superclass.constructor.call(this);
10332 };
10333
10334 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10335     /*
10336      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10337      */
10338     tabPosition : "top",
10339     /*
10340      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10341      */
10342     currentTabWidth : 0,
10343     /*
10344      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10345      */
10346     minTabWidth : 40,
10347     /*
10348      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10349      */
10350     maxTabWidth : 250,
10351     /*
10352      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10353      */
10354     preferredTabWidth : 175,
10355     /*
10356      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10357      */
10358     resizeTabs : false,
10359     /*
10360      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10361      */
10362     monitorResize : true,
10363     /*
10364      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10365      */
10366     toolbar : false,
10367
10368     /**
10369      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10370      * @param {String} id The id of the div to use <b>or create</b>
10371      * @param {String} text The text for the tab
10372      * @param {String} content (optional) Content to put in the TabPanelItem body
10373      * @param {Boolean} closable (optional) True to create a close icon on the tab
10374      * @return {Roo.TabPanelItem} The created TabPanelItem
10375      */
10376     addTab : function(id, text, content, closable){
10377         var item = new Roo.TabPanelItem(this, id, text, closable);
10378         this.addTabItem(item);
10379         if(content){
10380             item.setContent(content);
10381         }
10382         return item;
10383     },
10384
10385     /**
10386      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10387      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10388      * @return {Roo.TabPanelItem}
10389      */
10390     getTab : function(id){
10391         return this.items[id];
10392     },
10393
10394     /**
10395      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10396      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10397      */
10398     hideTab : function(id){
10399         var t = this.items[id];
10400         if(!t.isHidden()){
10401            t.setHidden(true);
10402            this.hiddenCount++;
10403            this.autoSizeTabs();
10404         }
10405     },
10406
10407     /**
10408      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10409      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10410      */
10411     unhideTab : function(id){
10412         var t = this.items[id];
10413         if(t.isHidden()){
10414            t.setHidden(false);
10415            this.hiddenCount--;
10416            this.autoSizeTabs();
10417         }
10418     },
10419
10420     /**
10421      * Adds an existing {@link Roo.TabPanelItem}.
10422      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10423      */
10424     addTabItem : function(item){
10425         this.items[item.id] = item;
10426         this.items.push(item);
10427         if(this.resizeTabs){
10428            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10429            this.autoSizeTabs();
10430         }else{
10431             item.autoSize();
10432         }
10433     },
10434
10435     /**
10436      * Removes a {@link Roo.TabPanelItem}.
10437      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10438      */
10439     removeTab : function(id){
10440         var items = this.items;
10441         var tab = items[id];
10442         if(!tab) { return; }
10443         var index = items.indexOf(tab);
10444         if(this.active == tab && items.length > 1){
10445             var newTab = this.getNextAvailable(index);
10446             if(newTab) {
10447                 newTab.activate();
10448             }
10449         }
10450         this.stripEl.dom.removeChild(tab.pnode.dom);
10451         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10452             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10453         }
10454         items.splice(index, 1);
10455         delete this.items[tab.id];
10456         tab.fireEvent("close", tab);
10457         tab.purgeListeners();
10458         this.autoSizeTabs();
10459     },
10460
10461     getNextAvailable : function(start){
10462         var items = this.items;
10463         var index = start;
10464         // look for a next tab that will slide over to
10465         // replace the one being removed
10466         while(index < items.length){
10467             var item = items[++index];
10468             if(item && !item.isHidden()){
10469                 return item;
10470             }
10471         }
10472         // if one isn't found select the previous tab (on the left)
10473         index = start;
10474         while(index >= 0){
10475             var item = items[--index];
10476             if(item && !item.isHidden()){
10477                 return item;
10478             }
10479         }
10480         return null;
10481     },
10482
10483     /**
10484      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10485      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10486      */
10487     disableTab : function(id){
10488         var tab = this.items[id];
10489         if(tab && this.active != tab){
10490             tab.disable();
10491         }
10492     },
10493
10494     /**
10495      * Enables a {@link Roo.TabPanelItem} that is disabled.
10496      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10497      */
10498     enableTab : function(id){
10499         var tab = this.items[id];
10500         tab.enable();
10501     },
10502
10503     /**
10504      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10505      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10506      * @return {Roo.TabPanelItem} The TabPanelItem.
10507      */
10508     activate : function(id){
10509         var tab = this.items[id];
10510         if(!tab){
10511             return null;
10512         }
10513         if(tab == this.active || tab.disabled){
10514             return tab;
10515         }
10516         var e = {};
10517         this.fireEvent("beforetabchange", this, e, tab);
10518         if(e.cancel !== true && !tab.disabled){
10519             if(this.active){
10520                 this.active.hide();
10521             }
10522             this.active = this.items[id];
10523             this.active.show();
10524             this.fireEvent("tabchange", this, this.active);
10525         }
10526         return tab;
10527     },
10528
10529     /**
10530      * Gets the active {@link Roo.TabPanelItem}.
10531      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10532      */
10533     getActiveTab : function(){
10534         return this.active;
10535     },
10536
10537     /**
10538      * Updates the tab body element to fit the height of the container element
10539      * for overflow scrolling
10540      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10541      */
10542     syncHeight : function(targetHeight){
10543         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10544         var bm = this.bodyEl.getMargins();
10545         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10546         this.bodyEl.setHeight(newHeight);
10547         return newHeight;
10548     },
10549
10550     onResize : function(){
10551         if(this.monitorResize){
10552             this.autoSizeTabs();
10553         }
10554     },
10555
10556     /**
10557      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10558      */
10559     beginUpdate : function(){
10560         this.updating = true;
10561     },
10562
10563     /**
10564      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10565      */
10566     endUpdate : function(){
10567         this.updating = false;
10568         this.autoSizeTabs();
10569     },
10570
10571     /**
10572      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10573      */
10574     autoSizeTabs : function(){
10575         var count = this.items.length;
10576         var vcount = count - this.hiddenCount;
10577         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
10578             return;
10579         }
10580         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10581         var availWidth = Math.floor(w / vcount);
10582         var b = this.stripBody;
10583         if(b.getWidth() > w){
10584             var tabs = this.items;
10585             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10586             if(availWidth < this.minTabWidth){
10587                 /*if(!this.sleft){    // incomplete scrolling code
10588                     this.createScrollButtons();
10589                 }
10590                 this.showScroll();
10591                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10592             }
10593         }else{
10594             if(this.currentTabWidth < this.preferredTabWidth){
10595                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10596             }
10597         }
10598     },
10599
10600     /**
10601      * Returns the number of tabs in this TabPanel.
10602      * @return {Number}
10603      */
10604      getCount : function(){
10605          return this.items.length;
10606      },
10607
10608     /**
10609      * Resizes all the tabs to the passed width
10610      * @param {Number} The new width
10611      */
10612     setTabWidth : function(width){
10613         this.currentTabWidth = width;
10614         for(var i = 0, len = this.items.length; i < len; i++) {
10615                 if(!this.items[i].isHidden()) {
10616                 this.items[i].setWidth(width);
10617             }
10618         }
10619     },
10620
10621     /**
10622      * Destroys this TabPanel
10623      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10624      */
10625     destroy : function(removeEl){
10626         Roo.EventManager.removeResizeListener(this.onResize, this);
10627         for(var i = 0, len = this.items.length; i < len; i++){
10628             this.items[i].purgeListeners();
10629         }
10630         if(removeEl === true){
10631             this.el.update("");
10632             this.el.remove();
10633         }
10634     }
10635 });
10636
10637 /**
10638  * @class Roo.TabPanelItem
10639  * @extends Roo.util.Observable
10640  * Represents an individual item (tab plus body) in a TabPanel.
10641  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10642  * @param {String} id The id of this TabPanelItem
10643  * @param {String} text The text for the tab of this TabPanelItem
10644  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10645  */
10646 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10647     /**
10648      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10649      * @type Roo.TabPanel
10650      */
10651     this.tabPanel = tabPanel;
10652     /**
10653      * The id for this TabPanelItem
10654      * @type String
10655      */
10656     this.id = id;
10657     /** @private */
10658     this.disabled = false;
10659     /** @private */
10660     this.text = text;
10661     /** @private */
10662     this.loaded = false;
10663     this.closable = closable;
10664
10665     /**
10666      * The body element for this TabPanelItem.
10667      * @type Roo.Element
10668      */
10669     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10670     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10671     this.bodyEl.setStyle("display", "block");
10672     this.bodyEl.setStyle("zoom", "1");
10673     this.hideAction();
10674
10675     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10676     /** @private */
10677     this.el = Roo.get(els.el, true);
10678     this.inner = Roo.get(els.inner, true);
10679     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10680     this.pnode = Roo.get(els.el.parentNode, true);
10681     this.el.on("mousedown", this.onTabMouseDown, this);
10682     this.el.on("click", this.onTabClick, this);
10683     /** @private */
10684     if(closable){
10685         var c = Roo.get(els.close, true);
10686         c.dom.title = this.closeText;
10687         c.addClassOnOver("close-over");
10688         c.on("click", this.closeClick, this);
10689      }
10690
10691     this.addEvents({
10692          /**
10693          * @event activate
10694          * Fires when this tab becomes the active tab.
10695          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10696          * @param {Roo.TabPanelItem} this
10697          */
10698         "activate": true,
10699         /**
10700          * @event beforeclose
10701          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10702          * @param {Roo.TabPanelItem} this
10703          * @param {Object} e Set cancel to true on this object to cancel the close.
10704          */
10705         "beforeclose": true,
10706         /**
10707          * @event close
10708          * Fires when this tab is closed.
10709          * @param {Roo.TabPanelItem} this
10710          */
10711          "close": true,
10712         /**
10713          * @event deactivate
10714          * Fires when this tab is no longer the active tab.
10715          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10716          * @param {Roo.TabPanelItem} this
10717          */
10718          "deactivate" : true
10719     });
10720     this.hidden = false;
10721
10722     Roo.TabPanelItem.superclass.constructor.call(this);
10723 };
10724
10725 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10726     purgeListeners : function(){
10727        Roo.util.Observable.prototype.purgeListeners.call(this);
10728        this.el.removeAllListeners();
10729     },
10730     /**
10731      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10732      */
10733     show : function(){
10734         this.pnode.addClass("on");
10735         this.showAction();
10736         if(Roo.isOpera){
10737             this.tabPanel.stripWrap.repaint();
10738         }
10739         this.fireEvent("activate", this.tabPanel, this);
10740     },
10741
10742     /**
10743      * Returns true if this tab is the active tab.
10744      * @return {Boolean}
10745      */
10746     isActive : function(){
10747         return this.tabPanel.getActiveTab() == this;
10748     },
10749
10750     /**
10751      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10752      */
10753     hide : function(){
10754         this.pnode.removeClass("on");
10755         this.hideAction();
10756         this.fireEvent("deactivate", this.tabPanel, this);
10757     },
10758
10759     hideAction : function(){
10760         this.bodyEl.hide();
10761         this.bodyEl.setStyle("position", "absolute");
10762         this.bodyEl.setLeft("-20000px");
10763         this.bodyEl.setTop("-20000px");
10764     },
10765
10766     showAction : function(){
10767         this.bodyEl.setStyle("position", "relative");
10768         this.bodyEl.setTop("");
10769         this.bodyEl.setLeft("");
10770         this.bodyEl.show();
10771     },
10772
10773     /**
10774      * Set the tooltip for the tab.
10775      * @param {String} tooltip The tab's tooltip
10776      */
10777     setTooltip : function(text){
10778         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10779             this.textEl.dom.qtip = text;
10780             this.textEl.dom.removeAttribute('title');
10781         }else{
10782             this.textEl.dom.title = text;
10783         }
10784     },
10785
10786     onTabClick : function(e){
10787         e.preventDefault();
10788         this.tabPanel.activate(this.id);
10789     },
10790
10791     onTabMouseDown : function(e){
10792         e.preventDefault();
10793         this.tabPanel.activate(this.id);
10794     },
10795
10796     getWidth : function(){
10797         return this.inner.getWidth();
10798     },
10799
10800     setWidth : function(width){
10801         var iwidth = width - this.pnode.getPadding("lr");
10802         this.inner.setWidth(iwidth);
10803         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10804         this.pnode.setWidth(width);
10805     },
10806
10807     /**
10808      * Show or hide the tab
10809      * @param {Boolean} hidden True to hide or false to show.
10810      */
10811     setHidden : function(hidden){
10812         this.hidden = hidden;
10813         this.pnode.setStyle("display", hidden ? "none" : "");
10814     },
10815
10816     /**
10817      * Returns true if this tab is "hidden"
10818      * @return {Boolean}
10819      */
10820     isHidden : function(){
10821         return this.hidden;
10822     },
10823
10824     /**
10825      * Returns the text for this tab
10826      * @return {String}
10827      */
10828     getText : function(){
10829         return this.text;
10830     },
10831
10832     autoSize : function(){
10833         //this.el.beginMeasure();
10834         this.textEl.setWidth(1);
10835         /*
10836          *  #2804 [new] Tabs in Roojs
10837          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10838          */
10839         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10840         //this.el.endMeasure();
10841     },
10842
10843     /**
10844      * Sets the text for the tab (Note: this also sets the tooltip text)
10845      * @param {String} text The tab's text and tooltip
10846      */
10847     setText : function(text){
10848         this.text = text;
10849         this.textEl.update(text);
10850         this.setTooltip(text);
10851         if(!this.tabPanel.resizeTabs){
10852             this.autoSize();
10853         }
10854     },
10855     /**
10856      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10857      */
10858     activate : function(){
10859         this.tabPanel.activate(this.id);
10860     },
10861
10862     /**
10863      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10864      */
10865     disable : function(){
10866         if(this.tabPanel.active != this){
10867             this.disabled = true;
10868             this.pnode.addClass("disabled");
10869         }
10870     },
10871
10872     /**
10873      * Enables this TabPanelItem if it was previously disabled.
10874      */
10875     enable : function(){
10876         this.disabled = false;
10877         this.pnode.removeClass("disabled");
10878     },
10879
10880     /**
10881      * Sets the content for this TabPanelItem.
10882      * @param {String} content The content
10883      * @param {Boolean} loadScripts true to look for and load scripts
10884      */
10885     setContent : function(content, loadScripts){
10886         this.bodyEl.update(content, loadScripts);
10887     },
10888
10889     /**
10890      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10891      * @return {Roo.UpdateManager} The UpdateManager
10892      */
10893     getUpdateManager : function(){
10894         return this.bodyEl.getUpdateManager();
10895     },
10896
10897     /**
10898      * Set a URL to be used to load the content for this TabPanelItem.
10899      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10900      * @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)
10901      * @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)
10902      * @return {Roo.UpdateManager} The UpdateManager
10903      */
10904     setUrl : function(url, params, loadOnce){
10905         if(this.refreshDelegate){
10906             this.un('activate', this.refreshDelegate);
10907         }
10908         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10909         this.on("activate", this.refreshDelegate);
10910         return this.bodyEl.getUpdateManager();
10911     },
10912
10913     /** @private */
10914     _handleRefresh : function(url, params, loadOnce){
10915         if(!loadOnce || !this.loaded){
10916             var updater = this.bodyEl.getUpdateManager();
10917             updater.update(url, params, this._setLoaded.createDelegate(this));
10918         }
10919     },
10920
10921     /**
10922      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10923      *   Will fail silently if the setUrl method has not been called.
10924      *   This does not activate the panel, just updates its content.
10925      */
10926     refresh : function(){
10927         if(this.refreshDelegate){
10928            this.loaded = false;
10929            this.refreshDelegate();
10930         }
10931     },
10932
10933     /** @private */
10934     _setLoaded : function(){
10935         this.loaded = true;
10936     },
10937
10938     /** @private */
10939     closeClick : function(e){
10940         var o = {};
10941         e.stopEvent();
10942         this.fireEvent("beforeclose", this, o);
10943         if(o.cancel !== true){
10944             this.tabPanel.removeTab(this.id);
10945         }
10946     },
10947     /**
10948      * The text displayed in the tooltip for the close icon.
10949      * @type String
10950      */
10951     closeText : "Close this tab"
10952 });
10953
10954 /** @private */
10955 Roo.TabPanel.prototype.createStrip = function(container){
10956     var strip = document.createElement("div");
10957     strip.className = "x-tabs-wrap";
10958     container.appendChild(strip);
10959     return strip;
10960 };
10961 /** @private */
10962 Roo.TabPanel.prototype.createStripList = function(strip){
10963     // div wrapper for retard IE
10964     // returns the "tr" element.
10965     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10966         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10967         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10968     return strip.firstChild.firstChild.firstChild.firstChild;
10969 };
10970 /** @private */
10971 Roo.TabPanel.prototype.createBody = function(container){
10972     var body = document.createElement("div");
10973     Roo.id(body, "tab-body");
10974     Roo.fly(body).addClass("x-tabs-body");
10975     container.appendChild(body);
10976     return body;
10977 };
10978 /** @private */
10979 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10980     var body = Roo.getDom(id);
10981     if(!body){
10982         body = document.createElement("div");
10983         body.id = id;
10984     }
10985     Roo.fly(body).addClass("x-tabs-item-body");
10986     bodyEl.insertBefore(body, bodyEl.firstChild);
10987     return body;
10988 };
10989 /** @private */
10990 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10991     var td = document.createElement("td");
10992     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10993     //stripEl.appendChild(td);
10994     if(closable){
10995         td.className = "x-tabs-closable";
10996         if(!this.closeTpl){
10997             this.closeTpl = new Roo.Template(
10998                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10999                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11000                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11001             );
11002         }
11003         var el = this.closeTpl.overwrite(td, {"text": text});
11004         var close = el.getElementsByTagName("div")[0];
11005         var inner = el.getElementsByTagName("em")[0];
11006         return {"el": el, "close": close, "inner": inner};
11007     } else {
11008         if(!this.tabTpl){
11009             this.tabTpl = new Roo.Template(
11010                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11011                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11012             );
11013         }
11014         var el = this.tabTpl.overwrite(td, {"text": text});
11015         var inner = el.getElementsByTagName("em")[0];
11016         return {"el": el, "inner": inner};
11017     }
11018 };/*
11019  * Based on:
11020  * Ext JS Library 1.1.1
11021  * Copyright(c) 2006-2007, Ext JS, LLC.
11022  *
11023  * Originally Released Under LGPL - original licence link has changed is not relivant.
11024  *
11025  * Fork - LGPL
11026  * <script type="text/javascript">
11027  */
11028
11029 /**
11030  * @class Roo.Button
11031  * @extends Roo.util.Observable
11032  * Simple Button class
11033  * @cfg {String} text The button text
11034  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11035  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11036  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11037  * @cfg {Object} scope The scope of the handler
11038  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11039  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11040  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11041  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11042  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11043  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11044    applies if enableToggle = true)
11045  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11046  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11047   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11048  * @constructor
11049  * Create a new button
11050  * @param {Object} config The config object
11051  */
11052 Roo.Button = function(renderTo, config)
11053 {
11054     if (!config) {
11055         config = renderTo;
11056         renderTo = config.renderTo || false;
11057     }
11058     
11059     Roo.apply(this, config);
11060     this.addEvents({
11061         /**
11062              * @event click
11063              * Fires when this button is clicked
11064              * @param {Button} this
11065              * @param {EventObject} e The click event
11066              */
11067             "click" : true,
11068         /**
11069              * @event toggle
11070              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11071              * @param {Button} this
11072              * @param {Boolean} pressed
11073              */
11074             "toggle" : true,
11075         /**
11076              * @event mouseover
11077              * Fires when the mouse hovers over the button
11078              * @param {Button} this
11079              * @param {Event} e The event object
11080              */
11081         'mouseover' : true,
11082         /**
11083              * @event mouseout
11084              * Fires when the mouse exits the button
11085              * @param {Button} this
11086              * @param {Event} e The event object
11087              */
11088         'mouseout': true,
11089          /**
11090              * @event render
11091              * Fires when the button is rendered
11092              * @param {Button} this
11093              */
11094         'render': true
11095     });
11096     if(this.menu){
11097         this.menu = Roo.menu.MenuMgr.get(this.menu);
11098     }
11099     // register listeners first!!  - so render can be captured..
11100     Roo.util.Observable.call(this);
11101     if(renderTo){
11102         this.render(renderTo);
11103     }
11104     
11105   
11106 };
11107
11108 Roo.extend(Roo.Button, Roo.util.Observable, {
11109     /**
11110      * 
11111      */
11112     
11113     /**
11114      * Read-only. True if this button is hidden
11115      * @type Boolean
11116      */
11117     hidden : false,
11118     /**
11119      * Read-only. True if this button is disabled
11120      * @type Boolean
11121      */
11122     disabled : false,
11123     /**
11124      * Read-only. True if this button is pressed (only if enableToggle = true)
11125      * @type Boolean
11126      */
11127     pressed : false,
11128
11129     /**
11130      * @cfg {Number} tabIndex 
11131      * The DOM tabIndex for this button (defaults to undefined)
11132      */
11133     tabIndex : undefined,
11134
11135     /**
11136      * @cfg {Boolean} enableToggle
11137      * True to enable pressed/not pressed toggling (defaults to false)
11138      */
11139     enableToggle: false,
11140     /**
11141      * @cfg {Mixed} menu
11142      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11143      */
11144     menu : undefined,
11145     /**
11146      * @cfg {String} menuAlign
11147      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11148      */
11149     menuAlign : "tl-bl?",
11150
11151     /**
11152      * @cfg {String} iconCls
11153      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11154      */
11155     iconCls : undefined,
11156     /**
11157      * @cfg {String} type
11158      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11159      */
11160     type : 'button',
11161
11162     // private
11163     menuClassTarget: 'tr',
11164
11165     /**
11166      * @cfg {String} clickEvent
11167      * The type of event to map to the button's event handler (defaults to 'click')
11168      */
11169     clickEvent : 'click',
11170
11171     /**
11172      * @cfg {Boolean} handleMouseEvents
11173      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11174      */
11175     handleMouseEvents : true,
11176
11177     /**
11178      * @cfg {String} tooltipType
11179      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11180      */
11181     tooltipType : 'qtip',
11182
11183     /**
11184      * @cfg {String} cls
11185      * A CSS class to apply to the button's main element.
11186      */
11187     
11188     /**
11189      * @cfg {Roo.Template} template (Optional)
11190      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11191      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11192      * require code modifications if required elements (e.g. a button) aren't present.
11193      */
11194
11195     // private
11196     render : function(renderTo){
11197         var btn;
11198         if(this.hideParent){
11199             this.parentEl = Roo.get(renderTo);
11200         }
11201         if(!this.dhconfig){
11202             if(!this.template){
11203                 if(!Roo.Button.buttonTemplate){
11204                     // hideous table template
11205                     Roo.Button.buttonTemplate = new Roo.Template(
11206                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11207                         '<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>',
11208                         "</tr></tbody></table>");
11209                 }
11210                 this.template = Roo.Button.buttonTemplate;
11211             }
11212             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11213             var btnEl = btn.child("button:first");
11214             btnEl.on('focus', this.onFocus, this);
11215             btnEl.on('blur', this.onBlur, this);
11216             if(this.cls){
11217                 btn.addClass(this.cls);
11218             }
11219             if(this.icon){
11220                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11221             }
11222             if(this.iconCls){
11223                 btnEl.addClass(this.iconCls);
11224                 if(!this.cls){
11225                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11226                 }
11227             }
11228             if(this.tabIndex !== undefined){
11229                 btnEl.dom.tabIndex = this.tabIndex;
11230             }
11231             if(this.tooltip){
11232                 if(typeof this.tooltip == 'object'){
11233                     Roo.QuickTips.tips(Roo.apply({
11234                           target: btnEl.id
11235                     }, this.tooltip));
11236                 } else {
11237                     btnEl.dom[this.tooltipType] = this.tooltip;
11238                 }
11239             }
11240         }else{
11241             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11242         }
11243         this.el = btn;
11244         if(this.id){
11245             this.el.dom.id = this.el.id = this.id;
11246         }
11247         if(this.menu){
11248             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11249             this.menu.on("show", this.onMenuShow, this);
11250             this.menu.on("hide", this.onMenuHide, this);
11251         }
11252         btn.addClass("x-btn");
11253         if(Roo.isIE && !Roo.isIE7){
11254             this.autoWidth.defer(1, this);
11255         }else{
11256             this.autoWidth();
11257         }
11258         if(this.handleMouseEvents){
11259             btn.on("mouseover", this.onMouseOver, this);
11260             btn.on("mouseout", this.onMouseOut, this);
11261             btn.on("mousedown", this.onMouseDown, this);
11262         }
11263         btn.on(this.clickEvent, this.onClick, this);
11264         //btn.on("mouseup", this.onMouseUp, this);
11265         if(this.hidden){
11266             this.hide();
11267         }
11268         if(this.disabled){
11269             this.disable();
11270         }
11271         Roo.ButtonToggleMgr.register(this);
11272         if(this.pressed){
11273             this.el.addClass("x-btn-pressed");
11274         }
11275         if(this.repeat){
11276             var repeater = new Roo.util.ClickRepeater(btn,
11277                 typeof this.repeat == "object" ? this.repeat : {}
11278             );
11279             repeater.on("click", this.onClick,  this);
11280         }
11281         
11282         this.fireEvent('render', this);
11283         
11284     },
11285     /**
11286      * Returns the button's underlying element
11287      * @return {Roo.Element} The element
11288      */
11289     getEl : function(){
11290         return this.el;  
11291     },
11292     
11293     /**
11294      * Destroys this Button and removes any listeners.
11295      */
11296     destroy : function(){
11297         Roo.ButtonToggleMgr.unregister(this);
11298         this.el.removeAllListeners();
11299         this.purgeListeners();
11300         this.el.remove();
11301     },
11302
11303     // private
11304     autoWidth : function(){
11305         if(this.el){
11306             this.el.setWidth("auto");
11307             if(Roo.isIE7 && Roo.isStrict){
11308                 var ib = this.el.child('button');
11309                 if(ib && ib.getWidth() > 20){
11310                     ib.clip();
11311                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11312                 }
11313             }
11314             if(this.minWidth){
11315                 if(this.hidden){
11316                     this.el.beginMeasure();
11317                 }
11318                 if(this.el.getWidth() < this.minWidth){
11319                     this.el.setWidth(this.minWidth);
11320                 }
11321                 if(this.hidden){
11322                     this.el.endMeasure();
11323                 }
11324             }
11325         }
11326     },
11327
11328     /**
11329      * Assigns this button's click handler
11330      * @param {Function} handler The function to call when the button is clicked
11331      * @param {Object} scope (optional) Scope for the function passed in
11332      */
11333     setHandler : function(handler, scope){
11334         this.handler = handler;
11335         this.scope = scope;  
11336     },
11337     
11338     /**
11339      * Sets this button's text
11340      * @param {String} text The button text
11341      */
11342     setText : function(text){
11343         this.text = text;
11344         if(this.el){
11345             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11346         }
11347         this.autoWidth();
11348     },
11349     
11350     /**
11351      * Gets the text for this button
11352      * @return {String} The button text
11353      */
11354     getText : function(){
11355         return this.text;  
11356     },
11357     
11358     /**
11359      * Show this button
11360      */
11361     show: function(){
11362         this.hidden = false;
11363         if(this.el){
11364             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11365         }
11366     },
11367     
11368     /**
11369      * Hide this button
11370      */
11371     hide: function(){
11372         this.hidden = true;
11373         if(this.el){
11374             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11375         }
11376     },
11377     
11378     /**
11379      * Convenience function for boolean show/hide
11380      * @param {Boolean} visible True to show, false to hide
11381      */
11382     setVisible: function(visible){
11383         if(visible) {
11384             this.show();
11385         }else{
11386             this.hide();
11387         }
11388     },
11389     
11390     /**
11391      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11392      * @param {Boolean} state (optional) Force a particular state
11393      */
11394     toggle : function(state){
11395         state = state === undefined ? !this.pressed : state;
11396         if(state != this.pressed){
11397             if(state){
11398                 this.el.addClass("x-btn-pressed");
11399                 this.pressed = true;
11400                 this.fireEvent("toggle", this, true);
11401             }else{
11402                 this.el.removeClass("x-btn-pressed");
11403                 this.pressed = false;
11404                 this.fireEvent("toggle", this, false);
11405             }
11406             if(this.toggleHandler){
11407                 this.toggleHandler.call(this.scope || this, this, state);
11408             }
11409         }
11410     },
11411     
11412     /**
11413      * Focus the button
11414      */
11415     focus : function(){
11416         this.el.child('button:first').focus();
11417     },
11418     
11419     /**
11420      * Disable this button
11421      */
11422     disable : function(){
11423         if(this.el){
11424             this.el.addClass("x-btn-disabled");
11425         }
11426         this.disabled = true;
11427     },
11428     
11429     /**
11430      * Enable this button
11431      */
11432     enable : function(){
11433         if(this.el){
11434             this.el.removeClass("x-btn-disabled");
11435         }
11436         this.disabled = false;
11437     },
11438
11439     /**
11440      * Convenience function for boolean enable/disable
11441      * @param {Boolean} enabled True to enable, false to disable
11442      */
11443     setDisabled : function(v){
11444         this[v !== true ? "enable" : "disable"]();
11445     },
11446
11447     // private
11448     onClick : function(e)
11449     {
11450         if(e){
11451             e.preventDefault();
11452         }
11453         if(e.button != 0){
11454             return;
11455         }
11456         if(!this.disabled){
11457             if(this.enableToggle){
11458                 this.toggle();
11459             }
11460             if(this.menu && !this.menu.isVisible()){
11461                 this.menu.show(this.el, this.menuAlign);
11462             }
11463             this.fireEvent("click", this, e);
11464             if(this.handler){
11465                 this.el.removeClass("x-btn-over");
11466                 this.handler.call(this.scope || this, this, e);
11467             }
11468         }
11469     },
11470     // private
11471     onMouseOver : function(e){
11472         if(!this.disabled){
11473             this.el.addClass("x-btn-over");
11474             this.fireEvent('mouseover', this, e);
11475         }
11476     },
11477     // private
11478     onMouseOut : function(e){
11479         if(!e.within(this.el,  true)){
11480             this.el.removeClass("x-btn-over");
11481             this.fireEvent('mouseout', this, e);
11482         }
11483     },
11484     // private
11485     onFocus : function(e){
11486         if(!this.disabled){
11487             this.el.addClass("x-btn-focus");
11488         }
11489     },
11490     // private
11491     onBlur : function(e){
11492         this.el.removeClass("x-btn-focus");
11493     },
11494     // private
11495     onMouseDown : function(e){
11496         if(!this.disabled && e.button == 0){
11497             this.el.addClass("x-btn-click");
11498             Roo.get(document).on('mouseup', this.onMouseUp, this);
11499         }
11500     },
11501     // private
11502     onMouseUp : function(e){
11503         if(e.button == 0){
11504             this.el.removeClass("x-btn-click");
11505             Roo.get(document).un('mouseup', this.onMouseUp, this);
11506         }
11507     },
11508     // private
11509     onMenuShow : function(e){
11510         this.el.addClass("x-btn-menu-active");
11511     },
11512     // private
11513     onMenuHide : function(e){
11514         this.el.removeClass("x-btn-menu-active");
11515     }   
11516 });
11517
11518 // Private utility class used by Button
11519 Roo.ButtonToggleMgr = function(){
11520    var groups = {};
11521    
11522    function toggleGroup(btn, state){
11523        if(state){
11524            var g = groups[btn.toggleGroup];
11525            for(var i = 0, l = g.length; i < l; i++){
11526                if(g[i] != btn){
11527                    g[i].toggle(false);
11528                }
11529            }
11530        }
11531    }
11532    
11533    return {
11534        register : function(btn){
11535            if(!btn.toggleGroup){
11536                return;
11537            }
11538            var g = groups[btn.toggleGroup];
11539            if(!g){
11540                g = groups[btn.toggleGroup] = [];
11541            }
11542            g.push(btn);
11543            btn.on("toggle", toggleGroup);
11544        },
11545        
11546        unregister : function(btn){
11547            if(!btn.toggleGroup){
11548                return;
11549            }
11550            var g = groups[btn.toggleGroup];
11551            if(g){
11552                g.remove(btn);
11553                btn.un("toggle", toggleGroup);
11554            }
11555        }
11556    };
11557 }();/*
11558  * Based on:
11559  * Ext JS Library 1.1.1
11560  * Copyright(c) 2006-2007, Ext JS, LLC.
11561  *
11562  * Originally Released Under LGPL - original licence link has changed is not relivant.
11563  *
11564  * Fork - LGPL
11565  * <script type="text/javascript">
11566  */
11567  
11568 /**
11569  * @class Roo.SplitButton
11570  * @extends Roo.Button
11571  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11572  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11573  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11574  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11575  * @cfg {String} arrowTooltip The title attribute of the arrow
11576  * @constructor
11577  * Create a new menu button
11578  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11579  * @param {Object} config The config object
11580  */
11581 Roo.SplitButton = function(renderTo, config){
11582     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11583     /**
11584      * @event arrowclick
11585      * Fires when this button's arrow is clicked
11586      * @param {SplitButton} this
11587      * @param {EventObject} e The click event
11588      */
11589     this.addEvents({"arrowclick":true});
11590 };
11591
11592 Roo.extend(Roo.SplitButton, Roo.Button, {
11593     render : function(renderTo){
11594         // this is one sweet looking template!
11595         var tpl = new Roo.Template(
11596             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11597             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11598             '<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>',
11599             "</tbody></table></td><td>",
11600             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11601             '<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>',
11602             "</tbody></table></td></tr></table>"
11603         );
11604         var btn = tpl.append(renderTo, [this.text, this.type], true);
11605         var btnEl = btn.child("button");
11606         if(this.cls){
11607             btn.addClass(this.cls);
11608         }
11609         if(this.icon){
11610             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11611         }
11612         if(this.iconCls){
11613             btnEl.addClass(this.iconCls);
11614             if(!this.cls){
11615                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11616             }
11617         }
11618         this.el = btn;
11619         if(this.handleMouseEvents){
11620             btn.on("mouseover", this.onMouseOver, this);
11621             btn.on("mouseout", this.onMouseOut, this);
11622             btn.on("mousedown", this.onMouseDown, this);
11623             btn.on("mouseup", this.onMouseUp, this);
11624         }
11625         btn.on(this.clickEvent, this.onClick, this);
11626         if(this.tooltip){
11627             if(typeof this.tooltip == 'object'){
11628                 Roo.QuickTips.tips(Roo.apply({
11629                       target: btnEl.id
11630                 }, this.tooltip));
11631             } else {
11632                 btnEl.dom[this.tooltipType] = this.tooltip;
11633             }
11634         }
11635         if(this.arrowTooltip){
11636             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11637         }
11638         if(this.hidden){
11639             this.hide();
11640         }
11641         if(this.disabled){
11642             this.disable();
11643         }
11644         if(this.pressed){
11645             this.el.addClass("x-btn-pressed");
11646         }
11647         if(Roo.isIE && !Roo.isIE7){
11648             this.autoWidth.defer(1, this);
11649         }else{
11650             this.autoWidth();
11651         }
11652         if(this.menu){
11653             this.menu.on("show", this.onMenuShow, this);
11654             this.menu.on("hide", this.onMenuHide, this);
11655         }
11656         this.fireEvent('render', this);
11657     },
11658
11659     // private
11660     autoWidth : function(){
11661         if(this.el){
11662             var tbl = this.el.child("table:first");
11663             var tbl2 = this.el.child("table:last");
11664             this.el.setWidth("auto");
11665             tbl.setWidth("auto");
11666             if(Roo.isIE7 && Roo.isStrict){
11667                 var ib = this.el.child('button:first');
11668                 if(ib && ib.getWidth() > 20){
11669                     ib.clip();
11670                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11671                 }
11672             }
11673             if(this.minWidth){
11674                 if(this.hidden){
11675                     this.el.beginMeasure();
11676                 }
11677                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11678                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11679                 }
11680                 if(this.hidden){
11681                     this.el.endMeasure();
11682                 }
11683             }
11684             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11685         } 
11686     },
11687     /**
11688      * Sets this button's click handler
11689      * @param {Function} handler The function to call when the button is clicked
11690      * @param {Object} scope (optional) Scope for the function passed above
11691      */
11692     setHandler : function(handler, scope){
11693         this.handler = handler;
11694         this.scope = scope;  
11695     },
11696     
11697     /**
11698      * Sets this button's arrow click handler
11699      * @param {Function} handler The function to call when the arrow is clicked
11700      * @param {Object} scope (optional) Scope for the function passed above
11701      */
11702     setArrowHandler : function(handler, scope){
11703         this.arrowHandler = handler;
11704         this.scope = scope;  
11705     },
11706     
11707     /**
11708      * Focus the button
11709      */
11710     focus : function(){
11711         if(this.el){
11712             this.el.child("button:first").focus();
11713         }
11714     },
11715
11716     // private
11717     onClick : function(e){
11718         e.preventDefault();
11719         if(!this.disabled){
11720             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11721                 if(this.menu && !this.menu.isVisible()){
11722                     this.menu.show(this.el, this.menuAlign);
11723                 }
11724                 this.fireEvent("arrowclick", this, e);
11725                 if(this.arrowHandler){
11726                     this.arrowHandler.call(this.scope || this, this, e);
11727                 }
11728             }else{
11729                 this.fireEvent("click", this, e);
11730                 if(this.handler){
11731                     this.handler.call(this.scope || this, this, e);
11732                 }
11733             }
11734         }
11735     },
11736     // private
11737     onMouseDown : function(e){
11738         if(!this.disabled){
11739             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11740         }
11741     },
11742     // private
11743     onMouseUp : function(e){
11744         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11745     }   
11746 });
11747
11748
11749 // backwards compat
11750 Roo.MenuButton = Roo.SplitButton;/*
11751  * Based on:
11752  * Ext JS Library 1.1.1
11753  * Copyright(c) 2006-2007, Ext JS, LLC.
11754  *
11755  * Originally Released Under LGPL - original licence link has changed is not relivant.
11756  *
11757  * Fork - LGPL
11758  * <script type="text/javascript">
11759  */
11760
11761 /**
11762  * @class Roo.Toolbar
11763  * Basic Toolbar class.
11764  * @constructor
11765  * Creates a new Toolbar
11766  * @param {Object} container The config object
11767  */ 
11768 Roo.Toolbar = function(container, buttons, config)
11769 {
11770     /// old consturctor format still supported..
11771     if(container instanceof Array){ // omit the container for later rendering
11772         buttons = container;
11773         config = buttons;
11774         container = null;
11775     }
11776     if (typeof(container) == 'object' && container.xtype) {
11777         config = container;
11778         container = config.container;
11779         buttons = config.buttons || []; // not really - use items!!
11780     }
11781     var xitems = [];
11782     if (config && config.items) {
11783         xitems = config.items;
11784         delete config.items;
11785     }
11786     Roo.apply(this, config);
11787     this.buttons = buttons;
11788     
11789     if(container){
11790         this.render(container);
11791     }
11792     this.xitems = xitems;
11793     Roo.each(xitems, function(b) {
11794         this.add(b);
11795     }, this);
11796     
11797 };
11798
11799 Roo.Toolbar.prototype = {
11800     /**
11801      * @cfg {Array} items
11802      * array of button configs or elements to add (will be converted to a MixedCollection)
11803      */
11804     
11805     /**
11806      * @cfg {String/HTMLElement/Element} container
11807      * The id or element that will contain the toolbar
11808      */
11809     // private
11810     render : function(ct){
11811         this.el = Roo.get(ct);
11812         if(this.cls){
11813             this.el.addClass(this.cls);
11814         }
11815         // using a table allows for vertical alignment
11816         // 100% width is needed by Safari...
11817         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11818         this.tr = this.el.child("tr", true);
11819         var autoId = 0;
11820         this.items = new Roo.util.MixedCollection(false, function(o){
11821             return o.id || ("item" + (++autoId));
11822         });
11823         if(this.buttons){
11824             this.add.apply(this, this.buttons);
11825             delete this.buttons;
11826         }
11827     },
11828
11829     /**
11830      * Adds element(s) to the toolbar -- this function takes a variable number of 
11831      * arguments of mixed type and adds them to the toolbar.
11832      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11833      * <ul>
11834      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11835      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11836      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11837      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11838      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11839      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11840      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11841      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11842      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11843      * </ul>
11844      * @param {Mixed} arg2
11845      * @param {Mixed} etc.
11846      */
11847     add : function(){
11848         var a = arguments, l = a.length;
11849         for(var i = 0; i < l; i++){
11850             this._add(a[i]);
11851         }
11852     },
11853     // private..
11854     _add : function(el) {
11855         
11856         if (el.xtype) {
11857             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11858         }
11859         
11860         if (el.applyTo){ // some kind of form field
11861             return this.addField(el);
11862         } 
11863         if (el.render){ // some kind of Toolbar.Item
11864             return this.addItem(el);
11865         }
11866         if (typeof el == "string"){ // string
11867             if(el == "separator" || el == "-"){
11868                 return this.addSeparator();
11869             }
11870             if (el == " "){
11871                 return this.addSpacer();
11872             }
11873             if(el == "->"){
11874                 return this.addFill();
11875             }
11876             return this.addText(el);
11877             
11878         }
11879         if(el.tagName){ // element
11880             return this.addElement(el);
11881         }
11882         if(typeof el == "object"){ // must be button config?
11883             return this.addButton(el);
11884         }
11885         // and now what?!?!
11886         return false;
11887         
11888     },
11889     
11890     /**
11891      * Add an Xtype element
11892      * @param {Object} xtype Xtype Object
11893      * @return {Object} created Object
11894      */
11895     addxtype : function(e){
11896         return this.add(e);  
11897     },
11898     
11899     /**
11900      * Returns the Element for this toolbar.
11901      * @return {Roo.Element}
11902      */
11903     getEl : function(){
11904         return this.el;  
11905     },
11906     
11907     /**
11908      * Adds a separator
11909      * @return {Roo.Toolbar.Item} The separator item
11910      */
11911     addSeparator : function(){
11912         return this.addItem(new Roo.Toolbar.Separator());
11913     },
11914
11915     /**
11916      * Adds a spacer element
11917      * @return {Roo.Toolbar.Spacer} The spacer item
11918      */
11919     addSpacer : function(){
11920         return this.addItem(new Roo.Toolbar.Spacer());
11921     },
11922
11923     /**
11924      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11925      * @return {Roo.Toolbar.Fill} The fill item
11926      */
11927     addFill : function(){
11928         return this.addItem(new Roo.Toolbar.Fill());
11929     },
11930
11931     /**
11932      * Adds any standard HTML element to the toolbar
11933      * @param {String/HTMLElement/Element} el The element or id of the element to add
11934      * @return {Roo.Toolbar.Item} The element's item
11935      */
11936     addElement : function(el){
11937         return this.addItem(new Roo.Toolbar.Item(el));
11938     },
11939     /**
11940      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11941      * @type Roo.util.MixedCollection  
11942      */
11943     items : false,
11944      
11945     /**
11946      * Adds any Toolbar.Item or subclass
11947      * @param {Roo.Toolbar.Item} item
11948      * @return {Roo.Toolbar.Item} The item
11949      */
11950     addItem : function(item){
11951         var td = this.nextBlock();
11952         item.render(td);
11953         this.items.add(item);
11954         return item;
11955     },
11956     
11957     /**
11958      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11959      * @param {Object/Array} config A button config or array of configs
11960      * @return {Roo.Toolbar.Button/Array}
11961      */
11962     addButton : function(config){
11963         if(config instanceof Array){
11964             var buttons = [];
11965             for(var i = 0, len = config.length; i < len; i++) {
11966                 buttons.push(this.addButton(config[i]));
11967             }
11968             return buttons;
11969         }
11970         var b = config;
11971         if(!(config instanceof Roo.Toolbar.Button)){
11972             b = config.split ?
11973                 new Roo.Toolbar.SplitButton(config) :
11974                 new Roo.Toolbar.Button(config);
11975         }
11976         var td = this.nextBlock();
11977         b.render(td);
11978         this.items.add(b);
11979         return b;
11980     },
11981     
11982     /**
11983      * Adds text to the toolbar
11984      * @param {String} text The text to add
11985      * @return {Roo.Toolbar.Item} The element's item
11986      */
11987     addText : function(text){
11988         return this.addItem(new Roo.Toolbar.TextItem(text));
11989     },
11990     
11991     /**
11992      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11993      * @param {Number} index The index where the item is to be inserted
11994      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11995      * @return {Roo.Toolbar.Button/Item}
11996      */
11997     insertButton : function(index, item){
11998         if(item instanceof Array){
11999             var buttons = [];
12000             for(var i = 0, len = item.length; i < len; i++) {
12001                buttons.push(this.insertButton(index + i, item[i]));
12002             }
12003             return buttons;
12004         }
12005         if (!(item instanceof Roo.Toolbar.Button)){
12006            item = new Roo.Toolbar.Button(item);
12007         }
12008         var td = document.createElement("td");
12009         this.tr.insertBefore(td, this.tr.childNodes[index]);
12010         item.render(td);
12011         this.items.insert(index, item);
12012         return item;
12013     },
12014     
12015     /**
12016      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12017      * @param {Object} config
12018      * @return {Roo.Toolbar.Item} The element's item
12019      */
12020     addDom : function(config, returnEl){
12021         var td = this.nextBlock();
12022         Roo.DomHelper.overwrite(td, config);
12023         var ti = new Roo.Toolbar.Item(td.firstChild);
12024         ti.render(td);
12025         this.items.add(ti);
12026         return ti;
12027     },
12028
12029     /**
12030      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12031      * @type Roo.util.MixedCollection  
12032      */
12033     fields : false,
12034     
12035     /**
12036      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12037      * Note: the field should not have been rendered yet. For a field that has already been
12038      * rendered, use {@link #addElement}.
12039      * @param {Roo.form.Field} field
12040      * @return {Roo.ToolbarItem}
12041      */
12042      
12043       
12044     addField : function(field) {
12045         if (!this.fields) {
12046             var autoId = 0;
12047             this.fields = new Roo.util.MixedCollection(false, function(o){
12048                 return o.id || ("item" + (++autoId));
12049             });
12050
12051         }
12052         
12053         var td = this.nextBlock();
12054         field.render(td);
12055         var ti = new Roo.Toolbar.Item(td.firstChild);
12056         ti.render(td);
12057         this.items.add(ti);
12058         this.fields.add(field);
12059         return ti;
12060     },
12061     /**
12062      * Hide the toolbar
12063      * @method hide
12064      */
12065      
12066       
12067     hide : function()
12068     {
12069         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12070         this.el.child('div').hide();
12071     },
12072     /**
12073      * Show the toolbar
12074      * @method show
12075      */
12076     show : function()
12077     {
12078         this.el.child('div').show();
12079     },
12080       
12081     // private
12082     nextBlock : function(){
12083         var td = document.createElement("td");
12084         this.tr.appendChild(td);
12085         return td;
12086     },
12087
12088     // private
12089     destroy : function(){
12090         if(this.items){ // rendered?
12091             Roo.destroy.apply(Roo, this.items.items);
12092         }
12093         if(this.fields){ // rendered?
12094             Roo.destroy.apply(Roo, this.fields.items);
12095         }
12096         Roo.Element.uncache(this.el, this.tr);
12097     }
12098 };
12099
12100 /**
12101  * @class Roo.Toolbar.Item
12102  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12103  * @constructor
12104  * Creates a new Item
12105  * @param {HTMLElement} el 
12106  */
12107 Roo.Toolbar.Item = function(el){
12108     var cfg = {};
12109     if (typeof (el.xtype) != 'undefined') {
12110         cfg = el;
12111         el = cfg.el;
12112     }
12113     
12114     this.el = Roo.getDom(el);
12115     this.id = Roo.id(this.el);
12116     this.hidden = false;
12117     
12118     this.addEvents({
12119          /**
12120              * @event render
12121              * Fires when the button is rendered
12122              * @param {Button} this
12123              */
12124         'render': true
12125     });
12126     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12127 };
12128 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12129 //Roo.Toolbar.Item.prototype = {
12130     
12131     /**
12132      * Get this item's HTML Element
12133      * @return {HTMLElement}
12134      */
12135     getEl : function(){
12136        return this.el;  
12137     },
12138
12139     // private
12140     render : function(td){
12141         
12142          this.td = td;
12143         td.appendChild(this.el);
12144         
12145         this.fireEvent('render', this);
12146     },
12147     
12148     /**
12149      * Removes and destroys this item.
12150      */
12151     destroy : function(){
12152         this.td.parentNode.removeChild(this.td);
12153     },
12154     
12155     /**
12156      * Shows this item.
12157      */
12158     show: function(){
12159         this.hidden = false;
12160         this.td.style.display = "";
12161     },
12162     
12163     /**
12164      * Hides this item.
12165      */
12166     hide: function(){
12167         this.hidden = true;
12168         this.td.style.display = "none";
12169     },
12170     
12171     /**
12172      * Convenience function for boolean show/hide.
12173      * @param {Boolean} visible true to show/false to hide
12174      */
12175     setVisible: function(visible){
12176         if(visible) {
12177             this.show();
12178         }else{
12179             this.hide();
12180         }
12181     },
12182     
12183     /**
12184      * Try to focus this item.
12185      */
12186     focus : function(){
12187         Roo.fly(this.el).focus();
12188     },
12189     
12190     /**
12191      * Disables this item.
12192      */
12193     disable : function(){
12194         Roo.fly(this.td).addClass("x-item-disabled");
12195         this.disabled = true;
12196         this.el.disabled = true;
12197     },
12198     
12199     /**
12200      * Enables this item.
12201      */
12202     enable : function(){
12203         Roo.fly(this.td).removeClass("x-item-disabled");
12204         this.disabled = false;
12205         this.el.disabled = false;
12206     }
12207 });
12208
12209
12210 /**
12211  * @class Roo.Toolbar.Separator
12212  * @extends Roo.Toolbar.Item
12213  * A simple toolbar separator class
12214  * @constructor
12215  * Creates a new Separator
12216  */
12217 Roo.Toolbar.Separator = function(cfg){
12218     
12219     var s = document.createElement("span");
12220     s.className = "ytb-sep";
12221     if (cfg) {
12222         cfg.el = s;
12223     }
12224     
12225     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12226 };
12227 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12228     enable:Roo.emptyFn,
12229     disable:Roo.emptyFn,
12230     focus:Roo.emptyFn
12231 });
12232
12233 /**
12234  * @class Roo.Toolbar.Spacer
12235  * @extends Roo.Toolbar.Item
12236  * A simple element that adds extra horizontal space to a toolbar.
12237  * @constructor
12238  * Creates a new Spacer
12239  */
12240 Roo.Toolbar.Spacer = function(cfg){
12241     var s = document.createElement("div");
12242     s.className = "ytb-spacer";
12243     if (cfg) {
12244         cfg.el = s;
12245     }
12246     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12247 };
12248 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12249     enable:Roo.emptyFn,
12250     disable:Roo.emptyFn,
12251     focus:Roo.emptyFn
12252 });
12253
12254 /**
12255  * @class Roo.Toolbar.Fill
12256  * @extends Roo.Toolbar.Spacer
12257  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12258  * @constructor
12259  * Creates a new Spacer
12260  */
12261 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12262     // private
12263     render : function(td){
12264         td.style.width = '100%';
12265         Roo.Toolbar.Fill.superclass.render.call(this, td);
12266     }
12267 });
12268
12269 /**
12270  * @class Roo.Toolbar.TextItem
12271  * @extends Roo.Toolbar.Item
12272  * A simple class that renders text directly into a toolbar.
12273  * @constructor
12274  * Creates a new TextItem
12275  * @param {String} text
12276  */
12277 Roo.Toolbar.TextItem = function(cfg){
12278     var  text = cfg || "";
12279     if (typeof(cfg) == 'object') {
12280         text = cfg.text || "";
12281     }  else {
12282         cfg = null;
12283     }
12284     var s = document.createElement("span");
12285     s.className = "ytb-text";
12286     s.innerHTML = text;
12287     if (cfg) {
12288         cfg.el  = s;
12289     }
12290     
12291     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12292 };
12293 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12294     
12295      
12296     enable:Roo.emptyFn,
12297     disable:Roo.emptyFn,
12298     focus:Roo.emptyFn
12299 });
12300
12301 /**
12302  * @class Roo.Toolbar.Button
12303  * @extends Roo.Button
12304  * A button that renders into a toolbar.
12305  * @constructor
12306  * Creates a new Button
12307  * @param {Object} config A standard {@link Roo.Button} config object
12308  */
12309 Roo.Toolbar.Button = function(config){
12310     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12311 };
12312 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12313     render : function(td){
12314         this.td = td;
12315         Roo.Toolbar.Button.superclass.render.call(this, td);
12316     },
12317     
12318     /**
12319      * Removes and destroys this button
12320      */
12321     destroy : function(){
12322         Roo.Toolbar.Button.superclass.destroy.call(this);
12323         this.td.parentNode.removeChild(this.td);
12324     },
12325     
12326     /**
12327      * Shows this button
12328      */
12329     show: function(){
12330         this.hidden = false;
12331         this.td.style.display = "";
12332     },
12333     
12334     /**
12335      * Hides this button
12336      */
12337     hide: function(){
12338         this.hidden = true;
12339         this.td.style.display = "none";
12340     },
12341
12342     /**
12343      * Disables this item
12344      */
12345     disable : function(){
12346         Roo.fly(this.td).addClass("x-item-disabled");
12347         this.disabled = true;
12348     },
12349
12350     /**
12351      * Enables this item
12352      */
12353     enable : function(){
12354         Roo.fly(this.td).removeClass("x-item-disabled");
12355         this.disabled = false;
12356     }
12357 });
12358 // backwards compat
12359 Roo.ToolbarButton = Roo.Toolbar.Button;
12360
12361 /**
12362  * @class Roo.Toolbar.SplitButton
12363  * @extends Roo.SplitButton
12364  * A menu button that renders into a toolbar.
12365  * @constructor
12366  * Creates a new SplitButton
12367  * @param {Object} config A standard {@link Roo.SplitButton} config object
12368  */
12369 Roo.Toolbar.SplitButton = function(config){
12370     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12371 };
12372 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12373     render : function(td){
12374         this.td = td;
12375         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12376     },
12377     
12378     /**
12379      * Removes and destroys this button
12380      */
12381     destroy : function(){
12382         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12383         this.td.parentNode.removeChild(this.td);
12384     },
12385     
12386     /**
12387      * Shows this button
12388      */
12389     show: function(){
12390         this.hidden = false;
12391         this.td.style.display = "";
12392     },
12393     
12394     /**
12395      * Hides this button
12396      */
12397     hide: function(){
12398         this.hidden = true;
12399         this.td.style.display = "none";
12400     }
12401 });
12402
12403 // backwards compat
12404 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12405  * Based on:
12406  * Ext JS Library 1.1.1
12407  * Copyright(c) 2006-2007, Ext JS, LLC.
12408  *
12409  * Originally Released Under LGPL - original licence link has changed is not relivant.
12410  *
12411  * Fork - LGPL
12412  * <script type="text/javascript">
12413  */
12414  
12415 /**
12416  * @class Roo.PagingToolbar
12417  * @extends Roo.Toolbar
12418  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12419  * @constructor
12420  * Create a new PagingToolbar
12421  * @param {Object} config The config object
12422  */
12423 Roo.PagingToolbar = function(el, ds, config)
12424 {
12425     // old args format still supported... - xtype is prefered..
12426     if (typeof(el) == 'object' && el.xtype) {
12427         // created from xtype...
12428         config = el;
12429         ds = el.dataSource;
12430         el = config.container;
12431     }
12432     var items = [];
12433     if (config.items) {
12434         items = config.items;
12435         config.items = [];
12436     }
12437     
12438     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12439     this.ds = ds;
12440     this.cursor = 0;
12441     this.renderButtons(this.el);
12442     this.bind(ds);
12443     
12444     // supprot items array.
12445    
12446     Roo.each(items, function(e) {
12447         this.add(Roo.factory(e));
12448     },this);
12449     
12450 };
12451
12452 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12453     /**
12454      * @cfg {Roo.data.Store} dataSource
12455      * The underlying data store providing the paged data
12456      */
12457     /**
12458      * @cfg {String/HTMLElement/Element} container
12459      * container The id or element that will contain the toolbar
12460      */
12461     /**
12462      * @cfg {Boolean} displayInfo
12463      * True to display the displayMsg (defaults to false)
12464      */
12465     /**
12466      * @cfg {Number} pageSize
12467      * The number of records to display per page (defaults to 20)
12468      */
12469     pageSize: 20,
12470     /**
12471      * @cfg {String} displayMsg
12472      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12473      */
12474     displayMsg : 'Displaying {0} - {1} of {2}',
12475     /**
12476      * @cfg {String} emptyMsg
12477      * The message to display when no records are found (defaults to "No data to display")
12478      */
12479     emptyMsg : 'No data to display',
12480     /**
12481      * Customizable piece of the default paging text (defaults to "Page")
12482      * @type String
12483      */
12484     beforePageText : "Page",
12485     /**
12486      * Customizable piece of the default paging text (defaults to "of %0")
12487      * @type String
12488      */
12489     afterPageText : "of {0}",
12490     /**
12491      * Customizable piece of the default paging text (defaults to "First Page")
12492      * @type String
12493      */
12494     firstText : "First Page",
12495     /**
12496      * Customizable piece of the default paging text (defaults to "Previous Page")
12497      * @type String
12498      */
12499     prevText : "Previous Page",
12500     /**
12501      * Customizable piece of the default paging text (defaults to "Next Page")
12502      * @type String
12503      */
12504     nextText : "Next Page",
12505     /**
12506      * Customizable piece of the default paging text (defaults to "Last Page")
12507      * @type String
12508      */
12509     lastText : "Last Page",
12510     /**
12511      * Customizable piece of the default paging text (defaults to "Refresh")
12512      * @type String
12513      */
12514     refreshText : "Refresh",
12515
12516     // private
12517     renderButtons : function(el){
12518         Roo.PagingToolbar.superclass.render.call(this, el);
12519         this.first = this.addButton({
12520             tooltip: this.firstText,
12521             cls: "x-btn-icon x-grid-page-first",
12522             disabled: true,
12523             handler: this.onClick.createDelegate(this, ["first"])
12524         });
12525         this.prev = this.addButton({
12526             tooltip: this.prevText,
12527             cls: "x-btn-icon x-grid-page-prev",
12528             disabled: true,
12529             handler: this.onClick.createDelegate(this, ["prev"])
12530         });
12531         //this.addSeparator();
12532         this.add(this.beforePageText);
12533         this.field = Roo.get(this.addDom({
12534            tag: "input",
12535            type: "text",
12536            size: "3",
12537            value: "1",
12538            cls: "x-grid-page-number"
12539         }).el);
12540         this.field.on("keydown", this.onPagingKeydown, this);
12541         this.field.on("focus", function(){this.dom.select();});
12542         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12543         this.field.setHeight(18);
12544         //this.addSeparator();
12545         this.next = this.addButton({
12546             tooltip: this.nextText,
12547             cls: "x-btn-icon x-grid-page-next",
12548             disabled: true,
12549             handler: this.onClick.createDelegate(this, ["next"])
12550         });
12551         this.last = this.addButton({
12552             tooltip: this.lastText,
12553             cls: "x-btn-icon x-grid-page-last",
12554             disabled: true,
12555             handler: this.onClick.createDelegate(this, ["last"])
12556         });
12557         //this.addSeparator();
12558         this.loading = this.addButton({
12559             tooltip: this.refreshText,
12560             cls: "x-btn-icon x-grid-loading",
12561             handler: this.onClick.createDelegate(this, ["refresh"])
12562         });
12563
12564         if(this.displayInfo){
12565             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12566         }
12567     },
12568
12569     // private
12570     updateInfo : function(){
12571         if(this.displayEl){
12572             var count = this.ds.getCount();
12573             var msg = count == 0 ?
12574                 this.emptyMsg :
12575                 String.format(
12576                     this.displayMsg,
12577                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12578                 );
12579             this.displayEl.update(msg);
12580         }
12581     },
12582
12583     // private
12584     onLoad : function(ds, r, o){
12585        this.cursor = o.params ? o.params.start : 0;
12586        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12587
12588        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12589        this.field.dom.value = ap;
12590        this.first.setDisabled(ap == 1);
12591        this.prev.setDisabled(ap == 1);
12592        this.next.setDisabled(ap == ps);
12593        this.last.setDisabled(ap == ps);
12594        this.loading.enable();
12595        this.updateInfo();
12596     },
12597
12598     // private
12599     getPageData : function(){
12600         var total = this.ds.getTotalCount();
12601         return {
12602             total : total,
12603             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12604             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12605         };
12606     },
12607
12608     // private
12609     onLoadError : function(){
12610         this.loading.enable();
12611     },
12612
12613     // private
12614     onPagingKeydown : function(e){
12615         var k = e.getKey();
12616         var d = this.getPageData();
12617         if(k == e.RETURN){
12618             var v = this.field.dom.value, pageNum;
12619             if(!v || isNaN(pageNum = parseInt(v, 10))){
12620                 this.field.dom.value = d.activePage;
12621                 return;
12622             }
12623             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12624             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12625             e.stopEvent();
12626         }
12627         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))
12628         {
12629           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12630           this.field.dom.value = pageNum;
12631           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12632           e.stopEvent();
12633         }
12634         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12635         {
12636           var v = this.field.dom.value, pageNum; 
12637           var increment = (e.shiftKey) ? 10 : 1;
12638           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
12639             increment *= -1;
12640           }
12641           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12642             this.field.dom.value = d.activePage;
12643             return;
12644           }
12645           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12646           {
12647             this.field.dom.value = parseInt(v, 10) + increment;
12648             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12649             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12650           }
12651           e.stopEvent();
12652         }
12653     },
12654
12655     // private
12656     beforeLoad : function(){
12657         if(this.loading){
12658             this.loading.disable();
12659         }
12660     },
12661
12662     // private
12663     onClick : function(which){
12664         var ds = this.ds;
12665         switch(which){
12666             case "first":
12667                 ds.load({params:{start: 0, limit: this.pageSize}});
12668             break;
12669             case "prev":
12670                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12671             break;
12672             case "next":
12673                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12674             break;
12675             case "last":
12676                 var total = ds.getTotalCount();
12677                 var extra = total % this.pageSize;
12678                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12679                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12680             break;
12681             case "refresh":
12682                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12683             break;
12684         }
12685     },
12686
12687     /**
12688      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12689      * @param {Roo.data.Store} store The data store to unbind
12690      */
12691     unbind : function(ds){
12692         ds.un("beforeload", this.beforeLoad, this);
12693         ds.un("load", this.onLoad, this);
12694         ds.un("loadexception", this.onLoadError, this);
12695         ds.un("remove", this.updateInfo, this);
12696         ds.un("add", this.updateInfo, this);
12697         this.ds = undefined;
12698     },
12699
12700     /**
12701      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12702      * @param {Roo.data.Store} store The data store to bind
12703      */
12704     bind : function(ds){
12705         ds.on("beforeload", this.beforeLoad, this);
12706         ds.on("load", this.onLoad, this);
12707         ds.on("loadexception", this.onLoadError, this);
12708         ds.on("remove", this.updateInfo, this);
12709         ds.on("add", this.updateInfo, this);
12710         this.ds = ds;
12711     }
12712 });/*
12713  * Based on:
12714  * Ext JS Library 1.1.1
12715  * Copyright(c) 2006-2007, Ext JS, LLC.
12716  *
12717  * Originally Released Under LGPL - original licence link has changed is not relivant.
12718  *
12719  * Fork - LGPL
12720  * <script type="text/javascript">
12721  */
12722
12723 /**
12724  * @class Roo.Resizable
12725  * @extends Roo.util.Observable
12726  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12727  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12728  * 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
12729  * the element will be wrapped for you automatically.</p>
12730  * <p>Here is the list of valid resize handles:</p>
12731  * <pre>
12732 Value   Description
12733 ------  -------------------
12734  'n'     north
12735  's'     south
12736  'e'     east
12737  'w'     west
12738  'nw'    northwest
12739  'sw'    southwest
12740  'se'    southeast
12741  'ne'    northeast
12742  'hd'    horizontal drag
12743  'all'   all
12744 </pre>
12745  * <p>Here's an example showing the creation of a typical Resizable:</p>
12746  * <pre><code>
12747 var resizer = new Roo.Resizable("element-id", {
12748     handles: 'all',
12749     minWidth: 200,
12750     minHeight: 100,
12751     maxWidth: 500,
12752     maxHeight: 400,
12753     pinned: true
12754 });
12755 resizer.on("resize", myHandler);
12756 </code></pre>
12757  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12758  * resizer.east.setDisplayed(false);</p>
12759  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12760  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12761  * resize operation's new size (defaults to [0, 0])
12762  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12763  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12764  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12765  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12766  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12767  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12768  * @cfg {Number} width The width of the element in pixels (defaults to null)
12769  * @cfg {Number} height The height of the element in pixels (defaults to null)
12770  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12771  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12772  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12773  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12774  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12775  * in favor of the handles config option (defaults to false)
12776  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12777  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12778  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12779  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12780  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12781  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12782  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12783  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12784  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12785  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12786  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12787  * @constructor
12788  * Create a new resizable component
12789  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12790  * @param {Object} config configuration options
12791   */
12792 Roo.Resizable = function(el, config)
12793 {
12794     this.el = Roo.get(el);
12795
12796     if(config && config.wrap){
12797         config.resizeChild = this.el;
12798         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12799         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12800         this.el.setStyle("overflow", "hidden");
12801         this.el.setPositioning(config.resizeChild.getPositioning());
12802         config.resizeChild.clearPositioning();
12803         if(!config.width || !config.height){
12804             var csize = config.resizeChild.getSize();
12805             this.el.setSize(csize.width, csize.height);
12806         }
12807         if(config.pinned && !config.adjustments){
12808             config.adjustments = "auto";
12809         }
12810     }
12811
12812     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12813     this.proxy.unselectable();
12814     this.proxy.enableDisplayMode('block');
12815
12816     Roo.apply(this, config);
12817
12818     if(this.pinned){
12819         this.disableTrackOver = true;
12820         this.el.addClass("x-resizable-pinned");
12821     }
12822     // if the element isn't positioned, make it relative
12823     var position = this.el.getStyle("position");
12824     if(position != "absolute" && position != "fixed"){
12825         this.el.setStyle("position", "relative");
12826     }
12827     if(!this.handles){ // no handles passed, must be legacy style
12828         this.handles = 's,e,se';
12829         if(this.multiDirectional){
12830             this.handles += ',n,w';
12831         }
12832     }
12833     if(this.handles == "all"){
12834         this.handles = "n s e w ne nw se sw";
12835     }
12836     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12837     var ps = Roo.Resizable.positions;
12838     for(var i = 0, len = hs.length; i < len; i++){
12839         if(hs[i] && ps[hs[i]]){
12840             var pos = ps[hs[i]];
12841             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12842         }
12843     }
12844     // legacy
12845     this.corner = this.southeast;
12846     
12847     // updateBox = the box can move..
12848     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12849         this.updateBox = true;
12850     }
12851
12852     this.activeHandle = null;
12853
12854     if(this.resizeChild){
12855         if(typeof this.resizeChild == "boolean"){
12856             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12857         }else{
12858             this.resizeChild = Roo.get(this.resizeChild, true);
12859         }
12860     }
12861     
12862     if(this.adjustments == "auto"){
12863         var rc = this.resizeChild;
12864         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12865         if(rc && (hw || hn)){
12866             rc.position("relative");
12867             rc.setLeft(hw ? hw.el.getWidth() : 0);
12868             rc.setTop(hn ? hn.el.getHeight() : 0);
12869         }
12870         this.adjustments = [
12871             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12872             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12873         ];
12874     }
12875
12876     if(this.draggable){
12877         this.dd = this.dynamic ?
12878             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12879         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12880     }
12881
12882     // public events
12883     this.addEvents({
12884         /**
12885          * @event beforeresize
12886          * Fired before resize is allowed. Set enabled to false to cancel resize.
12887          * @param {Roo.Resizable} this
12888          * @param {Roo.EventObject} e The mousedown event
12889          */
12890         "beforeresize" : true,
12891         /**
12892          * @event resizing
12893          * Fired a resizing.
12894          * @param {Roo.Resizable} this
12895          * @param {Number} x The new x position
12896          * @param {Number} y The new y position
12897          * @param {Number} w The new w width
12898          * @param {Number} h The new h hight
12899          * @param {Roo.EventObject} e The mouseup event
12900          */
12901         "resizing" : true,
12902         /**
12903          * @event resize
12904          * Fired after a resize.
12905          * @param {Roo.Resizable} this
12906          * @param {Number} width The new width
12907          * @param {Number} height The new height
12908          * @param {Roo.EventObject} e The mouseup event
12909          */
12910         "resize" : true
12911     });
12912
12913     if(this.width !== null && this.height !== null){
12914         this.resizeTo(this.width, this.height);
12915     }else{
12916         this.updateChildSize();
12917     }
12918     if(Roo.isIE){
12919         this.el.dom.style.zoom = 1;
12920     }
12921     Roo.Resizable.superclass.constructor.call(this);
12922 };
12923
12924 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12925         resizeChild : false,
12926         adjustments : [0, 0],
12927         minWidth : 5,
12928         minHeight : 5,
12929         maxWidth : 10000,
12930         maxHeight : 10000,
12931         enabled : true,
12932         animate : false,
12933         duration : .35,
12934         dynamic : false,
12935         handles : false,
12936         multiDirectional : false,
12937         disableTrackOver : false,
12938         easing : 'easeOutStrong',
12939         widthIncrement : 0,
12940         heightIncrement : 0,
12941         pinned : false,
12942         width : null,
12943         height : null,
12944         preserveRatio : false,
12945         transparent: false,
12946         minX: 0,
12947         minY: 0,
12948         draggable: false,
12949
12950         /**
12951          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12952          */
12953         constrainTo: undefined,
12954         /**
12955          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12956          */
12957         resizeRegion: undefined,
12958
12959
12960     /**
12961      * Perform a manual resize
12962      * @param {Number} width
12963      * @param {Number} height
12964      */
12965     resizeTo : function(width, height){
12966         this.el.setSize(width, height);
12967         this.updateChildSize();
12968         this.fireEvent("resize", this, width, height, null);
12969     },
12970
12971     // private
12972     startSizing : function(e, handle){
12973         this.fireEvent("beforeresize", this, e);
12974         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12975
12976             if(!this.overlay){
12977                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12978                 this.overlay.unselectable();
12979                 this.overlay.enableDisplayMode("block");
12980                 this.overlay.on("mousemove", this.onMouseMove, this);
12981                 this.overlay.on("mouseup", this.onMouseUp, this);
12982             }
12983             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12984
12985             this.resizing = true;
12986             this.startBox = this.el.getBox();
12987             this.startPoint = e.getXY();
12988             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12989                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12990
12991             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12992             this.overlay.show();
12993
12994             if(this.constrainTo) {
12995                 var ct = Roo.get(this.constrainTo);
12996                 this.resizeRegion = ct.getRegion().adjust(
12997                     ct.getFrameWidth('t'),
12998                     ct.getFrameWidth('l'),
12999                     -ct.getFrameWidth('b'),
13000                     -ct.getFrameWidth('r')
13001                 );
13002             }
13003
13004             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13005             this.proxy.show();
13006             this.proxy.setBox(this.startBox);
13007             if(!this.dynamic){
13008                 this.proxy.setStyle('visibility', 'visible');
13009             }
13010         }
13011     },
13012
13013     // private
13014     onMouseDown : function(handle, e){
13015         if(this.enabled){
13016             e.stopEvent();
13017             this.activeHandle = handle;
13018             this.startSizing(e, handle);
13019         }
13020     },
13021
13022     // private
13023     onMouseUp : function(e){
13024         var size = this.resizeElement();
13025         this.resizing = false;
13026         this.handleOut();
13027         this.overlay.hide();
13028         this.proxy.hide();
13029         this.fireEvent("resize", this, size.width, size.height, e);
13030     },
13031
13032     // private
13033     updateChildSize : function(){
13034         
13035         if(this.resizeChild){
13036             var el = this.el;
13037             var child = this.resizeChild;
13038             var adj = this.adjustments;
13039             if(el.dom.offsetWidth){
13040                 var b = el.getSize(true);
13041                 child.setSize(b.width+adj[0], b.height+adj[1]);
13042             }
13043             // Second call here for IE
13044             // The first call enables instant resizing and
13045             // the second call corrects scroll bars if they
13046             // exist
13047             if(Roo.isIE){
13048                 setTimeout(function(){
13049                     if(el.dom.offsetWidth){
13050                         var b = el.getSize(true);
13051                         child.setSize(b.width+adj[0], b.height+adj[1]);
13052                     }
13053                 }, 10);
13054             }
13055         }
13056     },
13057
13058     // private
13059     snap : function(value, inc, min){
13060         if(!inc || !value) {
13061             return value;
13062         }
13063         var newValue = value;
13064         var m = value % inc;
13065         if(m > 0){
13066             if(m > (inc/2)){
13067                 newValue = value + (inc-m);
13068             }else{
13069                 newValue = value - m;
13070             }
13071         }
13072         return Math.max(min, newValue);
13073     },
13074
13075     // private
13076     resizeElement : function(){
13077         var box = this.proxy.getBox();
13078         if(this.updateBox){
13079             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13080         }else{
13081             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13082         }
13083         this.updateChildSize();
13084         if(!this.dynamic){
13085             this.proxy.hide();
13086         }
13087         return box;
13088     },
13089
13090     // private
13091     constrain : function(v, diff, m, mx){
13092         if(v - diff < m){
13093             diff = v - m;
13094         }else if(v - diff > mx){
13095             diff = mx - v;
13096         }
13097         return diff;
13098     },
13099
13100     // private
13101     onMouseMove : function(e){
13102         
13103         if(this.enabled){
13104             try{// try catch so if something goes wrong the user doesn't get hung
13105
13106             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13107                 return;
13108             }
13109
13110             //var curXY = this.startPoint;
13111             var curSize = this.curSize || this.startBox;
13112             var x = this.startBox.x, y = this.startBox.y;
13113             var ox = x, oy = y;
13114             var w = curSize.width, h = curSize.height;
13115             var ow = w, oh = h;
13116             var mw = this.minWidth, mh = this.minHeight;
13117             var mxw = this.maxWidth, mxh = this.maxHeight;
13118             var wi = this.widthIncrement;
13119             var hi = this.heightIncrement;
13120
13121             var eventXY = e.getXY();
13122             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13123             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13124
13125             var pos = this.activeHandle.position;
13126
13127             switch(pos){
13128                 case "east":
13129                     w += diffX;
13130                     w = Math.min(Math.max(mw, w), mxw);
13131                     break;
13132              
13133                 case "south":
13134                     h += diffY;
13135                     h = Math.min(Math.max(mh, h), mxh);
13136                     break;
13137                 case "southeast":
13138                     w += diffX;
13139                     h += diffY;
13140                     w = Math.min(Math.max(mw, w), mxw);
13141                     h = Math.min(Math.max(mh, h), mxh);
13142                     break;
13143                 case "north":
13144                     diffY = this.constrain(h, diffY, mh, mxh);
13145                     y += diffY;
13146                     h -= diffY;
13147                     break;
13148                 case "hdrag":
13149                     
13150                     if (wi) {
13151                         var adiffX = Math.abs(diffX);
13152                         var sub = (adiffX % wi); // how much 
13153                         if (sub > (wi/2)) { // far enough to snap
13154                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13155                         } else {
13156                             // remove difference.. 
13157                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13158                         }
13159                     }
13160                     x += diffX;
13161                     x = Math.max(this.minX, x);
13162                     break;
13163                 case "west":
13164                     diffX = this.constrain(w, diffX, mw, mxw);
13165                     x += diffX;
13166                     w -= diffX;
13167                     break;
13168                 case "northeast":
13169                     w += diffX;
13170                     w = Math.min(Math.max(mw, w), mxw);
13171                     diffY = this.constrain(h, diffY, mh, mxh);
13172                     y += diffY;
13173                     h -= diffY;
13174                     break;
13175                 case "northwest":
13176                     diffX = this.constrain(w, diffX, mw, mxw);
13177                     diffY = this.constrain(h, diffY, mh, mxh);
13178                     y += diffY;
13179                     h -= diffY;
13180                     x += diffX;
13181                     w -= diffX;
13182                     break;
13183                case "southwest":
13184                     diffX = this.constrain(w, diffX, mw, mxw);
13185                     h += diffY;
13186                     h = Math.min(Math.max(mh, h), mxh);
13187                     x += diffX;
13188                     w -= diffX;
13189                     break;
13190             }
13191
13192             var sw = this.snap(w, wi, mw);
13193             var sh = this.snap(h, hi, mh);
13194             if(sw != w || sh != h){
13195                 switch(pos){
13196                     case "northeast":
13197                         y -= sh - h;
13198                     break;
13199                     case "north":
13200                         y -= sh - h;
13201                         break;
13202                     case "southwest":
13203                         x -= sw - w;
13204                     break;
13205                     case "west":
13206                         x -= sw - w;
13207                         break;
13208                     case "northwest":
13209                         x -= sw - w;
13210                         y -= sh - h;
13211                     break;
13212                 }
13213                 w = sw;
13214                 h = sh;
13215             }
13216
13217             if(this.preserveRatio){
13218                 switch(pos){
13219                     case "southeast":
13220                     case "east":
13221                         h = oh * (w/ow);
13222                         h = Math.min(Math.max(mh, h), mxh);
13223                         w = ow * (h/oh);
13224                        break;
13225                     case "south":
13226                         w = ow * (h/oh);
13227                         w = Math.min(Math.max(mw, w), mxw);
13228                         h = oh * (w/ow);
13229                         break;
13230                     case "northeast":
13231                         w = ow * (h/oh);
13232                         w = Math.min(Math.max(mw, w), mxw);
13233                         h = oh * (w/ow);
13234                     break;
13235                     case "north":
13236                         var tw = w;
13237                         w = ow * (h/oh);
13238                         w = Math.min(Math.max(mw, w), mxw);
13239                         h = oh * (w/ow);
13240                         x += (tw - w) / 2;
13241                         break;
13242                     case "southwest":
13243                         h = oh * (w/ow);
13244                         h = Math.min(Math.max(mh, h), mxh);
13245                         var tw = w;
13246                         w = ow * (h/oh);
13247                         x += tw - w;
13248                         break;
13249                     case "west":
13250                         var th = h;
13251                         h = oh * (w/ow);
13252                         h = Math.min(Math.max(mh, h), mxh);
13253                         y += (th - h) / 2;
13254                         var tw = w;
13255                         w = ow * (h/oh);
13256                         x += tw - w;
13257                        break;
13258                     case "northwest":
13259                         var tw = w;
13260                         var th = h;
13261                         h = oh * (w/ow);
13262                         h = Math.min(Math.max(mh, h), mxh);
13263                         w = ow * (h/oh);
13264                         y += th - h;
13265                         x += tw - w;
13266                        break;
13267
13268                 }
13269             }
13270             if (pos == 'hdrag') {
13271                 w = ow;
13272             }
13273             this.proxy.setBounds(x, y, w, h);
13274             if(this.dynamic){
13275                 this.resizeElement();
13276             }
13277             }catch(e){}
13278         }
13279         this.fireEvent("resizing", this, x, y, w, h, e);
13280     },
13281
13282     // private
13283     handleOver : function(){
13284         if(this.enabled){
13285             this.el.addClass("x-resizable-over");
13286         }
13287     },
13288
13289     // private
13290     handleOut : function(){
13291         if(!this.resizing){
13292             this.el.removeClass("x-resizable-over");
13293         }
13294     },
13295
13296     /**
13297      * Returns the element this component is bound to.
13298      * @return {Roo.Element}
13299      */
13300     getEl : function(){
13301         return this.el;
13302     },
13303
13304     /**
13305      * Returns the resizeChild element (or null).
13306      * @return {Roo.Element}
13307      */
13308     getResizeChild : function(){
13309         return this.resizeChild;
13310     },
13311     groupHandler : function()
13312     {
13313         
13314     },
13315     /**
13316      * Destroys this resizable. If the element was wrapped and
13317      * removeEl is not true then the element remains.
13318      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13319      */
13320     destroy : function(removeEl){
13321         this.proxy.remove();
13322         if(this.overlay){
13323             this.overlay.removeAllListeners();
13324             this.overlay.remove();
13325         }
13326         var ps = Roo.Resizable.positions;
13327         for(var k in ps){
13328             if(typeof ps[k] != "function" && this[ps[k]]){
13329                 var h = this[ps[k]];
13330                 h.el.removeAllListeners();
13331                 h.el.remove();
13332             }
13333         }
13334         if(removeEl){
13335             this.el.update("");
13336             this.el.remove();
13337         }
13338     }
13339 });
13340
13341 // private
13342 // hash to map config positions to true positions
13343 Roo.Resizable.positions = {
13344     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13345     hd: "hdrag"
13346 };
13347
13348 // private
13349 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13350     if(!this.tpl){
13351         // only initialize the template if resizable is used
13352         var tpl = Roo.DomHelper.createTemplate(
13353             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13354         );
13355         tpl.compile();
13356         Roo.Resizable.Handle.prototype.tpl = tpl;
13357     }
13358     this.position = pos;
13359     this.rz = rz;
13360     // show north drag fro topdra
13361     var handlepos = pos == 'hdrag' ? 'north' : pos;
13362     
13363     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13364     if (pos == 'hdrag') {
13365         this.el.setStyle('cursor', 'pointer');
13366     }
13367     this.el.unselectable();
13368     if(transparent){
13369         this.el.setOpacity(0);
13370     }
13371     this.el.on("mousedown", this.onMouseDown, this);
13372     if(!disableTrackOver){
13373         this.el.on("mouseover", this.onMouseOver, this);
13374         this.el.on("mouseout", this.onMouseOut, this);
13375     }
13376 };
13377
13378 // private
13379 Roo.Resizable.Handle.prototype = {
13380     afterResize : function(rz){
13381         Roo.log('after?');
13382         // do nothing
13383     },
13384     // private
13385     onMouseDown : function(e){
13386         this.rz.onMouseDown(this, e);
13387     },
13388     // private
13389     onMouseOver : function(e){
13390         this.rz.handleOver(this, e);
13391     },
13392     // private
13393     onMouseOut : function(e){
13394         this.rz.handleOut(this, e);
13395     }
13396 };/*
13397  * Based on:
13398  * Ext JS Library 1.1.1
13399  * Copyright(c) 2006-2007, Ext JS, LLC.
13400  *
13401  * Originally Released Under LGPL - original licence link has changed is not relivant.
13402  *
13403  * Fork - LGPL
13404  * <script type="text/javascript">
13405  */
13406
13407 /**
13408  * @class Roo.Editor
13409  * @extends Roo.Component
13410  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13411  * @constructor
13412  * Create a new Editor
13413  * @param {Roo.form.Field} field The Field object (or descendant)
13414  * @param {Object} config The config object
13415  */
13416 Roo.Editor = function(field, config){
13417     Roo.Editor.superclass.constructor.call(this, config);
13418     this.field = field;
13419     this.addEvents({
13420         /**
13421              * @event beforestartedit
13422              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13423              * false from the handler of this event.
13424              * @param {Editor} this
13425              * @param {Roo.Element} boundEl The underlying element bound to this editor
13426              * @param {Mixed} value The field value being set
13427              */
13428         "beforestartedit" : true,
13429         /**
13430              * @event startedit
13431              * Fires when this editor is displayed
13432              * @param {Roo.Element} boundEl The underlying element bound to this editor
13433              * @param {Mixed} value The starting field value
13434              */
13435         "startedit" : true,
13436         /**
13437              * @event beforecomplete
13438              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13439              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13440              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13441              * event will not fire since no edit actually occurred.
13442              * @param {Editor} this
13443              * @param {Mixed} value The current field value
13444              * @param {Mixed} startValue The original field value
13445              */
13446         "beforecomplete" : true,
13447         /**
13448              * @event complete
13449              * Fires after editing is complete and any changed value has been written to the underlying field.
13450              * @param {Editor} this
13451              * @param {Mixed} value The current field value
13452              * @param {Mixed} startValue The original field value
13453              */
13454         "complete" : true,
13455         /**
13456          * @event specialkey
13457          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13458          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13459          * @param {Roo.form.Field} this
13460          * @param {Roo.EventObject} e The event object
13461          */
13462         "specialkey" : true
13463     });
13464 };
13465
13466 Roo.extend(Roo.Editor, Roo.Component, {
13467     /**
13468      * @cfg {Boolean/String} autosize
13469      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13470      * or "height" to adopt the height only (defaults to false)
13471      */
13472     /**
13473      * @cfg {Boolean} revertInvalid
13474      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13475      * validation fails (defaults to true)
13476      */
13477     /**
13478      * @cfg {Boolean} ignoreNoChange
13479      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13480      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13481      * will never be ignored.
13482      */
13483     /**
13484      * @cfg {Boolean} hideEl
13485      * False to keep the bound element visible while the editor is displayed (defaults to true)
13486      */
13487     /**
13488      * @cfg {Mixed} value
13489      * The data value of the underlying field (defaults to "")
13490      */
13491     value : "",
13492     /**
13493      * @cfg {String} alignment
13494      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13495      */
13496     alignment: "c-c?",
13497     /**
13498      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13499      * for bottom-right shadow (defaults to "frame")
13500      */
13501     shadow : "frame",
13502     /**
13503      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13504      */
13505     constrain : false,
13506     /**
13507      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13508      */
13509     completeOnEnter : false,
13510     /**
13511      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13512      */
13513     cancelOnEsc : false,
13514     /**
13515      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13516      */
13517     updateEl : false,
13518
13519     // private
13520     onRender : function(ct, position){
13521         this.el = new Roo.Layer({
13522             shadow: this.shadow,
13523             cls: "x-editor",
13524             parentEl : ct,
13525             shim : this.shim,
13526             shadowOffset:4,
13527             id: this.id,
13528             constrain: this.constrain
13529         });
13530         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13531         if(this.field.msgTarget != 'title'){
13532             this.field.msgTarget = 'qtip';
13533         }
13534         this.field.render(this.el);
13535         if(Roo.isGecko){
13536             this.field.el.dom.setAttribute('autocomplete', 'off');
13537         }
13538         this.field.on("specialkey", this.onSpecialKey, this);
13539         if(this.swallowKeys){
13540             this.field.el.swallowEvent(['keydown','keypress']);
13541         }
13542         this.field.show();
13543         this.field.on("blur", this.onBlur, this);
13544         if(this.field.grow){
13545             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13546         }
13547     },
13548
13549     onSpecialKey : function(field, e)
13550     {
13551         //Roo.log('editor onSpecialKey');
13552         if(this.completeOnEnter && e.getKey() == e.ENTER){
13553             e.stopEvent();
13554             this.completeEdit();
13555             return;
13556         }
13557         // do not fire special key otherwise it might hide close the editor...
13558         if(e.getKey() == e.ENTER){    
13559             return;
13560         }
13561         if(this.cancelOnEsc && e.getKey() == e.ESC){
13562             this.cancelEdit();
13563             return;
13564         } 
13565         this.fireEvent('specialkey', field, e);
13566     
13567     },
13568
13569     /**
13570      * Starts the editing process and shows the editor.
13571      * @param {String/HTMLElement/Element} el The element to edit
13572      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13573       * to the innerHTML of el.
13574      */
13575     startEdit : function(el, value){
13576         if(this.editing){
13577             this.completeEdit();
13578         }
13579         this.boundEl = Roo.get(el);
13580         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13581         if(!this.rendered){
13582             this.render(this.parentEl || document.body);
13583         }
13584         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13585             return;
13586         }
13587         this.startValue = v;
13588         this.field.setValue(v);
13589         if(this.autoSize){
13590             var sz = this.boundEl.getSize();
13591             switch(this.autoSize){
13592                 case "width":
13593                 this.setSize(sz.width,  "");
13594                 break;
13595                 case "height":
13596                 this.setSize("",  sz.height);
13597                 break;
13598                 default:
13599                 this.setSize(sz.width,  sz.height);
13600             }
13601         }
13602         this.el.alignTo(this.boundEl, this.alignment);
13603         this.editing = true;
13604         if(Roo.QuickTips){
13605             Roo.QuickTips.disable();
13606         }
13607         this.show();
13608     },
13609
13610     /**
13611      * Sets the height and width of this editor.
13612      * @param {Number} width The new width
13613      * @param {Number} height The new height
13614      */
13615     setSize : function(w, h){
13616         this.field.setSize(w, h);
13617         if(this.el){
13618             this.el.sync();
13619         }
13620     },
13621
13622     /**
13623      * Realigns the editor to the bound field based on the current alignment config value.
13624      */
13625     realign : function(){
13626         this.el.alignTo(this.boundEl, this.alignment);
13627     },
13628
13629     /**
13630      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13631      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13632      */
13633     completeEdit : function(remainVisible){
13634         if(!this.editing){
13635             return;
13636         }
13637         var v = this.getValue();
13638         if(this.revertInvalid !== false && !this.field.isValid()){
13639             v = this.startValue;
13640             this.cancelEdit(true);
13641         }
13642         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13643             this.editing = false;
13644             this.hide();
13645             return;
13646         }
13647         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13648             this.editing = false;
13649             if(this.updateEl && this.boundEl){
13650                 this.boundEl.update(v);
13651             }
13652             if(remainVisible !== true){
13653                 this.hide();
13654             }
13655             this.fireEvent("complete", this, v, this.startValue);
13656         }
13657     },
13658
13659     // private
13660     onShow : function(){
13661         this.el.show();
13662         if(this.hideEl !== false){
13663             this.boundEl.hide();
13664         }
13665         this.field.show();
13666         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13667             this.fixIEFocus = true;
13668             this.deferredFocus.defer(50, this);
13669         }else{
13670             this.field.focus();
13671         }
13672         this.fireEvent("startedit", this.boundEl, this.startValue);
13673     },
13674
13675     deferredFocus : function(){
13676         if(this.editing){
13677             this.field.focus();
13678         }
13679     },
13680
13681     /**
13682      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13683      * reverted to the original starting value.
13684      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13685      * cancel (defaults to false)
13686      */
13687     cancelEdit : function(remainVisible){
13688         if(this.editing){
13689             this.setValue(this.startValue);
13690             if(remainVisible !== true){
13691                 this.hide();
13692             }
13693         }
13694     },
13695
13696     // private
13697     onBlur : function(){
13698         if(this.allowBlur !== true && this.editing){
13699             this.completeEdit();
13700         }
13701     },
13702
13703     // private
13704     onHide : function(){
13705         if(this.editing){
13706             this.completeEdit();
13707             return;
13708         }
13709         this.field.blur();
13710         if(this.field.collapse){
13711             this.field.collapse();
13712         }
13713         this.el.hide();
13714         if(this.hideEl !== false){
13715             this.boundEl.show();
13716         }
13717         if(Roo.QuickTips){
13718             Roo.QuickTips.enable();
13719         }
13720     },
13721
13722     /**
13723      * Sets the data value of the editor
13724      * @param {Mixed} value Any valid value supported by the underlying field
13725      */
13726     setValue : function(v){
13727         this.field.setValue(v);
13728     },
13729
13730     /**
13731      * Gets the data value of the editor
13732      * @return {Mixed} The data value
13733      */
13734     getValue : function(){
13735         return this.field.getValue();
13736     }
13737 });/*
13738  * Based on:
13739  * Ext JS Library 1.1.1
13740  * Copyright(c) 2006-2007, Ext JS, LLC.
13741  *
13742  * Originally Released Under LGPL - original licence link has changed is not relivant.
13743  *
13744  * Fork - LGPL
13745  * <script type="text/javascript">
13746  */
13747  
13748 /**
13749  * @class Roo.BasicDialog
13750  * @extends Roo.util.Observable
13751  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13752  * <pre><code>
13753 var dlg = new Roo.BasicDialog("my-dlg", {
13754     height: 200,
13755     width: 300,
13756     minHeight: 100,
13757     minWidth: 150,
13758     modal: true,
13759     proxyDrag: true,
13760     shadow: true
13761 });
13762 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13763 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13764 dlg.addButton('Cancel', dlg.hide, dlg);
13765 dlg.show();
13766 </code></pre>
13767   <b>A Dialog should always be a direct child of the body element.</b>
13768  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13769  * @cfg {String} title Default text to display in the title bar (defaults to null)
13770  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13771  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13772  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13773  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13774  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13775  * (defaults to null with no animation)
13776  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13777  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13778  * property for valid values (defaults to 'all')
13779  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13780  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13781  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13782  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13783  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13784  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13785  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13786  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13787  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13788  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13789  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13790  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13791  * draggable = true (defaults to false)
13792  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13793  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13794  * shadow (defaults to false)
13795  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13796  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13797  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13798  * @cfg {Array} buttons Array of buttons
13799  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13800  * @constructor
13801  * Create a new BasicDialog.
13802  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13803  * @param {Object} config Configuration options
13804  */
13805 Roo.BasicDialog = function(el, config){
13806     this.el = Roo.get(el);
13807     var dh = Roo.DomHelper;
13808     if(!this.el && config && config.autoCreate){
13809         if(typeof config.autoCreate == "object"){
13810             if(!config.autoCreate.id){
13811                 config.autoCreate.id = el;
13812             }
13813             this.el = dh.append(document.body,
13814                         config.autoCreate, true);
13815         }else{
13816             this.el = dh.append(document.body,
13817                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13818         }
13819     }
13820     el = this.el;
13821     el.setDisplayed(true);
13822     el.hide = this.hideAction;
13823     this.id = el.id;
13824     el.addClass("x-dlg");
13825
13826     Roo.apply(this, config);
13827
13828     this.proxy = el.createProxy("x-dlg-proxy");
13829     this.proxy.hide = this.hideAction;
13830     this.proxy.setOpacity(.5);
13831     this.proxy.hide();
13832
13833     if(config.width){
13834         el.setWidth(config.width);
13835     }
13836     if(config.height){
13837         el.setHeight(config.height);
13838     }
13839     this.size = el.getSize();
13840     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13841         this.xy = [config.x,config.y];
13842     }else{
13843         this.xy = el.getCenterXY(true);
13844     }
13845     /** The header element @type Roo.Element */
13846     this.header = el.child("> .x-dlg-hd");
13847     /** The body element @type Roo.Element */
13848     this.body = el.child("> .x-dlg-bd");
13849     /** The footer element @type Roo.Element */
13850     this.footer = el.child("> .x-dlg-ft");
13851
13852     if(!this.header){
13853         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13854     }
13855     if(!this.body){
13856         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13857     }
13858
13859     this.header.unselectable();
13860     if(this.title){
13861         this.header.update(this.title);
13862     }
13863     // this element allows the dialog to be focused for keyboard event
13864     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13865     this.focusEl.swallowEvent("click", true);
13866
13867     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13868
13869     // wrap the body and footer for special rendering
13870     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13871     if(this.footer){
13872         this.bwrap.dom.appendChild(this.footer.dom);
13873     }
13874
13875     this.bg = this.el.createChild({
13876         tag: "div", cls:"x-dlg-bg",
13877         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13878     });
13879     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13880
13881
13882     if(this.autoScroll !== false && !this.autoTabs){
13883         this.body.setStyle("overflow", "auto");
13884     }
13885
13886     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13887
13888     if(this.closable !== false){
13889         this.el.addClass("x-dlg-closable");
13890         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13891         this.close.on("click", this.closeClick, this);
13892         this.close.addClassOnOver("x-dlg-close-over");
13893     }
13894     if(this.collapsible !== false){
13895         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13896         this.collapseBtn.on("click", this.collapseClick, this);
13897         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13898         this.header.on("dblclick", this.collapseClick, this);
13899     }
13900     if(this.resizable !== false){
13901         this.el.addClass("x-dlg-resizable");
13902         this.resizer = new Roo.Resizable(el, {
13903             minWidth: this.minWidth || 80,
13904             minHeight:this.minHeight || 80,
13905             handles: this.resizeHandles || "all",
13906             pinned: true
13907         });
13908         this.resizer.on("beforeresize", this.beforeResize, this);
13909         this.resizer.on("resize", this.onResize, this);
13910     }
13911     if(this.draggable !== false){
13912         el.addClass("x-dlg-draggable");
13913         if (!this.proxyDrag) {
13914             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13915         }
13916         else {
13917             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13918         }
13919         dd.setHandleElId(this.header.id);
13920         dd.endDrag = this.endMove.createDelegate(this);
13921         dd.startDrag = this.startMove.createDelegate(this);
13922         dd.onDrag = this.onDrag.createDelegate(this);
13923         dd.scroll = false;
13924         this.dd = dd;
13925     }
13926     if(this.modal){
13927         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13928         this.mask.enableDisplayMode("block");
13929         this.mask.hide();
13930         this.el.addClass("x-dlg-modal");
13931     }
13932     if(this.shadow){
13933         this.shadow = new Roo.Shadow({
13934             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13935             offset : this.shadowOffset
13936         });
13937     }else{
13938         this.shadowOffset = 0;
13939     }
13940     if(Roo.useShims && this.shim !== false){
13941         this.shim = this.el.createShim();
13942         this.shim.hide = this.hideAction;
13943         this.shim.hide();
13944     }else{
13945         this.shim = false;
13946     }
13947     if(this.autoTabs){
13948         this.initTabs();
13949     }
13950     if (this.buttons) { 
13951         var bts= this.buttons;
13952         this.buttons = [];
13953         Roo.each(bts, function(b) {
13954             this.addButton(b);
13955         }, this);
13956     }
13957     
13958     
13959     this.addEvents({
13960         /**
13961          * @event keydown
13962          * Fires when a key is pressed
13963          * @param {Roo.BasicDialog} this
13964          * @param {Roo.EventObject} e
13965          */
13966         "keydown" : true,
13967         /**
13968          * @event move
13969          * Fires when this dialog is moved by the user.
13970          * @param {Roo.BasicDialog} this
13971          * @param {Number} x The new page X
13972          * @param {Number} y The new page Y
13973          */
13974         "move" : true,
13975         /**
13976          * @event resize
13977          * Fires when this dialog is resized by the user.
13978          * @param {Roo.BasicDialog} this
13979          * @param {Number} width The new width
13980          * @param {Number} height The new height
13981          */
13982         "resize" : true,
13983         /**
13984          * @event beforehide
13985          * Fires before this dialog is hidden.
13986          * @param {Roo.BasicDialog} this
13987          */
13988         "beforehide" : true,
13989         /**
13990          * @event hide
13991          * Fires when this dialog is hidden.
13992          * @param {Roo.BasicDialog} this
13993          */
13994         "hide" : true,
13995         /**
13996          * @event beforeshow
13997          * Fires before this dialog is shown.
13998          * @param {Roo.BasicDialog} this
13999          */
14000         "beforeshow" : true,
14001         /**
14002          * @event show
14003          * Fires when this dialog is shown.
14004          * @param {Roo.BasicDialog} this
14005          */
14006         "show" : true
14007     });
14008     el.on("keydown", this.onKeyDown, this);
14009     el.on("mousedown", this.toFront, this);
14010     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14011     this.el.hide();
14012     Roo.DialogManager.register(this);
14013     Roo.BasicDialog.superclass.constructor.call(this);
14014 };
14015
14016 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14017     shadowOffset: Roo.isIE ? 6 : 5,
14018     minHeight: 80,
14019     minWidth: 200,
14020     minButtonWidth: 75,
14021     defaultButton: null,
14022     buttonAlign: "right",
14023     tabTag: 'div',
14024     firstShow: true,
14025
14026     /**
14027      * Sets the dialog title text
14028      * @param {String} text The title text to display
14029      * @return {Roo.BasicDialog} this
14030      */
14031     setTitle : function(text){
14032         this.header.update(text);
14033         return this;
14034     },
14035
14036     // private
14037     closeClick : function(){
14038         this.hide();
14039     },
14040
14041     // private
14042     collapseClick : function(){
14043         this[this.collapsed ? "expand" : "collapse"]();
14044     },
14045
14046     /**
14047      * Collapses the dialog to its minimized state (only the title bar is visible).
14048      * Equivalent to the user clicking the collapse dialog button.
14049      */
14050     collapse : function(){
14051         if(!this.collapsed){
14052             this.collapsed = true;
14053             this.el.addClass("x-dlg-collapsed");
14054             this.restoreHeight = this.el.getHeight();
14055             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14056         }
14057     },
14058
14059     /**
14060      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14061      * clicking the expand dialog button.
14062      */
14063     expand : function(){
14064         if(this.collapsed){
14065             this.collapsed = false;
14066             this.el.removeClass("x-dlg-collapsed");
14067             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14068         }
14069     },
14070
14071     /**
14072      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14073      * @return {Roo.TabPanel} The tabs component
14074      */
14075     initTabs : function(){
14076         var tabs = this.getTabs();
14077         while(tabs.getTab(0)){
14078             tabs.removeTab(0);
14079         }
14080         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14081             var dom = el.dom;
14082             tabs.addTab(Roo.id(dom), dom.title);
14083             dom.title = "";
14084         });
14085         tabs.activate(0);
14086         return tabs;
14087     },
14088
14089     // private
14090     beforeResize : function(){
14091         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14092     },
14093
14094     // private
14095     onResize : function(){
14096         this.refreshSize();
14097         this.syncBodyHeight();
14098         this.adjustAssets();
14099         this.focus();
14100         this.fireEvent("resize", this, this.size.width, this.size.height);
14101     },
14102
14103     // private
14104     onKeyDown : function(e){
14105         if(this.isVisible()){
14106             this.fireEvent("keydown", this, e);
14107         }
14108     },
14109
14110     /**
14111      * Resizes the dialog.
14112      * @param {Number} width
14113      * @param {Number} height
14114      * @return {Roo.BasicDialog} this
14115      */
14116     resizeTo : function(width, height){
14117         this.el.setSize(width, height);
14118         this.size = {width: width, height: height};
14119         this.syncBodyHeight();
14120         if(this.fixedcenter){
14121             this.center();
14122         }
14123         if(this.isVisible()){
14124             this.constrainXY();
14125             this.adjustAssets();
14126         }
14127         this.fireEvent("resize", this, width, height);
14128         return this;
14129     },
14130
14131
14132     /**
14133      * Resizes the dialog to fit the specified content size.
14134      * @param {Number} width
14135      * @param {Number} height
14136      * @return {Roo.BasicDialog} this
14137      */
14138     setContentSize : function(w, h){
14139         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14140         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14141         //if(!this.el.isBorderBox()){
14142             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14143             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14144         //}
14145         if(this.tabs){
14146             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14147             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14148         }
14149         this.resizeTo(w, h);
14150         return this;
14151     },
14152
14153     /**
14154      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14155      * executed in response to a particular key being pressed while the dialog is active.
14156      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14157      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14158      * @param {Function} fn The function to call
14159      * @param {Object} scope (optional) The scope of the function
14160      * @return {Roo.BasicDialog} this
14161      */
14162     addKeyListener : function(key, fn, scope){
14163         var keyCode, shift, ctrl, alt;
14164         if(typeof key == "object" && !(key instanceof Array)){
14165             keyCode = key["key"];
14166             shift = key["shift"];
14167             ctrl = key["ctrl"];
14168             alt = key["alt"];
14169         }else{
14170             keyCode = key;
14171         }
14172         var handler = function(dlg, e){
14173             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14174                 var k = e.getKey();
14175                 if(keyCode instanceof Array){
14176                     for(var i = 0, len = keyCode.length; i < len; i++){
14177                         if(keyCode[i] == k){
14178                           fn.call(scope || window, dlg, k, e);
14179                           return;
14180                         }
14181                     }
14182                 }else{
14183                     if(k == keyCode){
14184                         fn.call(scope || window, dlg, k, e);
14185                     }
14186                 }
14187             }
14188         };
14189         this.on("keydown", handler);
14190         return this;
14191     },
14192
14193     /**
14194      * Returns the TabPanel component (creates it if it doesn't exist).
14195      * Note: If you wish to simply check for the existence of tabs without creating them,
14196      * check for a null 'tabs' property.
14197      * @return {Roo.TabPanel} The tabs component
14198      */
14199     getTabs : function(){
14200         if(!this.tabs){
14201             this.el.addClass("x-dlg-auto-tabs");
14202             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14203             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14204         }
14205         return this.tabs;
14206     },
14207
14208     /**
14209      * Adds a button to the footer section of the dialog.
14210      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14211      * object or a valid Roo.DomHelper element config
14212      * @param {Function} handler The function called when the button is clicked
14213      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14214      * @return {Roo.Button} The new button
14215      */
14216     addButton : function(config, handler, scope){
14217         var dh = Roo.DomHelper;
14218         if(!this.footer){
14219             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14220         }
14221         if(!this.btnContainer){
14222             var tb = this.footer.createChild({
14223
14224                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14225                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14226             }, null, true);
14227             this.btnContainer = tb.firstChild.firstChild.firstChild;
14228         }
14229         var bconfig = {
14230             handler: handler,
14231             scope: scope,
14232             minWidth: this.minButtonWidth,
14233             hideParent:true
14234         };
14235         if(typeof config == "string"){
14236             bconfig.text = config;
14237         }else{
14238             if(config.tag){
14239                 bconfig.dhconfig = config;
14240             }else{
14241                 Roo.apply(bconfig, config);
14242             }
14243         }
14244         var fc = false;
14245         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14246             bconfig.position = Math.max(0, bconfig.position);
14247             fc = this.btnContainer.childNodes[bconfig.position];
14248         }
14249          
14250         var btn = new Roo.Button(
14251             fc ? 
14252                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14253                 : this.btnContainer.appendChild(document.createElement("td")),
14254             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14255             bconfig
14256         );
14257         this.syncBodyHeight();
14258         if(!this.buttons){
14259             /**
14260              * Array of all the buttons that have been added to this dialog via addButton
14261              * @type Array
14262              */
14263             this.buttons = [];
14264         }
14265         this.buttons.push(btn);
14266         return btn;
14267     },
14268
14269     /**
14270      * Sets the default button to be focused when the dialog is displayed.
14271      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14272      * @return {Roo.BasicDialog} this
14273      */
14274     setDefaultButton : function(btn){
14275         this.defaultButton = btn;
14276         return this;
14277     },
14278
14279     // private
14280     getHeaderFooterHeight : function(safe){
14281         var height = 0;
14282         if(this.header){
14283            height += this.header.getHeight();
14284         }
14285         if(this.footer){
14286            var fm = this.footer.getMargins();
14287             height += (this.footer.getHeight()+fm.top+fm.bottom);
14288         }
14289         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14290         height += this.centerBg.getPadding("tb");
14291         return height;
14292     },
14293
14294     // private
14295     syncBodyHeight : function()
14296     {
14297         var bd = this.body, // the text
14298             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14299             bw = this.bwrap;
14300         var height = this.size.height - this.getHeaderFooterHeight(false);
14301         bd.setHeight(height-bd.getMargins("tb"));
14302         var hh = this.header.getHeight();
14303         var h = this.size.height-hh;
14304         cb.setHeight(h);
14305         
14306         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14307         bw.setHeight(h-cb.getPadding("tb"));
14308         
14309         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14310         bd.setWidth(bw.getWidth(true));
14311         if(this.tabs){
14312             this.tabs.syncHeight();
14313             if(Roo.isIE){
14314                 this.tabs.el.repaint();
14315             }
14316         }
14317     },
14318
14319     /**
14320      * Restores the previous state of the dialog if Roo.state is configured.
14321      * @return {Roo.BasicDialog} this
14322      */
14323     restoreState : function(){
14324         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14325         if(box && box.width){
14326             this.xy = [box.x, box.y];
14327             this.resizeTo(box.width, box.height);
14328         }
14329         return this;
14330     },
14331
14332     // private
14333     beforeShow : function(){
14334         this.expand();
14335         if(this.fixedcenter){
14336             this.xy = this.el.getCenterXY(true);
14337         }
14338         if(this.modal){
14339             Roo.get(document.body).addClass("x-body-masked");
14340             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14341             this.mask.show();
14342         }
14343         this.constrainXY();
14344     },
14345
14346     // private
14347     animShow : function(){
14348         var b = Roo.get(this.animateTarget).getBox();
14349         this.proxy.setSize(b.width, b.height);
14350         this.proxy.setLocation(b.x, b.y);
14351         this.proxy.show();
14352         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14353                     true, .35, this.showEl.createDelegate(this));
14354     },
14355
14356     /**
14357      * Shows the dialog.
14358      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14359      * @return {Roo.BasicDialog} this
14360      */
14361     show : function(animateTarget){
14362         if (this.fireEvent("beforeshow", this) === false){
14363             return;
14364         }
14365         if(this.syncHeightBeforeShow){
14366             this.syncBodyHeight();
14367         }else if(this.firstShow){
14368             this.firstShow = false;
14369             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14370         }
14371         this.animateTarget = animateTarget || this.animateTarget;
14372         if(!this.el.isVisible()){
14373             this.beforeShow();
14374             if(this.animateTarget && Roo.get(this.animateTarget)){
14375                 this.animShow();
14376             }else{
14377                 this.showEl();
14378             }
14379         }
14380         return this;
14381     },
14382
14383     // private
14384     showEl : function(){
14385         this.proxy.hide();
14386         this.el.setXY(this.xy);
14387         this.el.show();
14388         this.adjustAssets(true);
14389         this.toFront();
14390         this.focus();
14391         // IE peekaboo bug - fix found by Dave Fenwick
14392         if(Roo.isIE){
14393             this.el.repaint();
14394         }
14395         this.fireEvent("show", this);
14396     },
14397
14398     /**
14399      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14400      * dialog itself will receive focus.
14401      */
14402     focus : function(){
14403         if(this.defaultButton){
14404             this.defaultButton.focus();
14405         }else{
14406             this.focusEl.focus();
14407         }
14408     },
14409
14410     // private
14411     constrainXY : function(){
14412         if(this.constraintoviewport !== false){
14413             if(!this.viewSize){
14414                 if(this.container){
14415                     var s = this.container.getSize();
14416                     this.viewSize = [s.width, s.height];
14417                 }else{
14418                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14419                 }
14420             }
14421             var s = Roo.get(this.container||document).getScroll();
14422
14423             var x = this.xy[0], y = this.xy[1];
14424             var w = this.size.width, h = this.size.height;
14425             var vw = this.viewSize[0], vh = this.viewSize[1];
14426             // only move it if it needs it
14427             var moved = false;
14428             // first validate right/bottom
14429             if(x + w > vw+s.left){
14430                 x = vw - w;
14431                 moved = true;
14432             }
14433             if(y + h > vh+s.top){
14434                 y = vh - h;
14435                 moved = true;
14436             }
14437             // then make sure top/left isn't negative
14438             if(x < s.left){
14439                 x = s.left;
14440                 moved = true;
14441             }
14442             if(y < s.top){
14443                 y = s.top;
14444                 moved = true;
14445             }
14446             if(moved){
14447                 // cache xy
14448                 this.xy = [x, y];
14449                 if(this.isVisible()){
14450                     this.el.setLocation(x, y);
14451                     this.adjustAssets();
14452                 }
14453             }
14454         }
14455     },
14456
14457     // private
14458     onDrag : function(){
14459         if(!this.proxyDrag){
14460             this.xy = this.el.getXY();
14461             this.adjustAssets();
14462         }
14463     },
14464
14465     // private
14466     adjustAssets : function(doShow){
14467         var x = this.xy[0], y = this.xy[1];
14468         var w = this.size.width, h = this.size.height;
14469         if(doShow === true){
14470             if(this.shadow){
14471                 this.shadow.show(this.el);
14472             }
14473             if(this.shim){
14474                 this.shim.show();
14475             }
14476         }
14477         if(this.shadow && this.shadow.isVisible()){
14478             this.shadow.show(this.el);
14479         }
14480         if(this.shim && this.shim.isVisible()){
14481             this.shim.setBounds(x, y, w, h);
14482         }
14483     },
14484
14485     // private
14486     adjustViewport : function(w, h){
14487         if(!w || !h){
14488             w = Roo.lib.Dom.getViewWidth();
14489             h = Roo.lib.Dom.getViewHeight();
14490         }
14491         // cache the size
14492         this.viewSize = [w, h];
14493         if(this.modal && this.mask.isVisible()){
14494             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14495             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14496         }
14497         if(this.isVisible()){
14498             this.constrainXY();
14499         }
14500     },
14501
14502     /**
14503      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14504      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14505      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14506      */
14507     destroy : function(removeEl){
14508         if(this.isVisible()){
14509             this.animateTarget = null;
14510             this.hide();
14511         }
14512         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14513         if(this.tabs){
14514             this.tabs.destroy(removeEl);
14515         }
14516         Roo.destroy(
14517              this.shim,
14518              this.proxy,
14519              this.resizer,
14520              this.close,
14521              this.mask
14522         );
14523         if(this.dd){
14524             this.dd.unreg();
14525         }
14526         if(this.buttons){
14527            for(var i = 0, len = this.buttons.length; i < len; i++){
14528                this.buttons[i].destroy();
14529            }
14530         }
14531         this.el.removeAllListeners();
14532         if(removeEl === true){
14533             this.el.update("");
14534             this.el.remove();
14535         }
14536         Roo.DialogManager.unregister(this);
14537     },
14538
14539     // private
14540     startMove : function(){
14541         if(this.proxyDrag){
14542             this.proxy.show();
14543         }
14544         if(this.constraintoviewport !== false){
14545             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14546         }
14547     },
14548
14549     // private
14550     endMove : function(){
14551         if(!this.proxyDrag){
14552             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14553         }else{
14554             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14555             this.proxy.hide();
14556         }
14557         this.refreshSize();
14558         this.adjustAssets();
14559         this.focus();
14560         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14561     },
14562
14563     /**
14564      * Brings this dialog to the front of any other visible dialogs
14565      * @return {Roo.BasicDialog} this
14566      */
14567     toFront : function(){
14568         Roo.DialogManager.bringToFront(this);
14569         return this;
14570     },
14571
14572     /**
14573      * Sends this dialog to the back (under) of any other visible dialogs
14574      * @return {Roo.BasicDialog} this
14575      */
14576     toBack : function(){
14577         Roo.DialogManager.sendToBack(this);
14578         return this;
14579     },
14580
14581     /**
14582      * Centers this dialog in the viewport
14583      * @return {Roo.BasicDialog} this
14584      */
14585     center : function(){
14586         var xy = this.el.getCenterXY(true);
14587         this.moveTo(xy[0], xy[1]);
14588         return this;
14589     },
14590
14591     /**
14592      * Moves the dialog's top-left corner to the specified point
14593      * @param {Number} x
14594      * @param {Number} y
14595      * @return {Roo.BasicDialog} this
14596      */
14597     moveTo : function(x, y){
14598         this.xy = [x,y];
14599         if(this.isVisible()){
14600             this.el.setXY(this.xy);
14601             this.adjustAssets();
14602         }
14603         return this;
14604     },
14605
14606     /**
14607      * Aligns the dialog to the specified element
14608      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14609      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14610      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14611      * @return {Roo.BasicDialog} this
14612      */
14613     alignTo : function(element, position, offsets){
14614         this.xy = this.el.getAlignToXY(element, position, offsets);
14615         if(this.isVisible()){
14616             this.el.setXY(this.xy);
14617             this.adjustAssets();
14618         }
14619         return this;
14620     },
14621
14622     /**
14623      * Anchors an element to another element and realigns it when the window is resized.
14624      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14625      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14626      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14627      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14628      * is a number, it is used as the buffer delay (defaults to 50ms).
14629      * @return {Roo.BasicDialog} this
14630      */
14631     anchorTo : function(el, alignment, offsets, monitorScroll){
14632         var action = function(){
14633             this.alignTo(el, alignment, offsets);
14634         };
14635         Roo.EventManager.onWindowResize(action, this);
14636         var tm = typeof monitorScroll;
14637         if(tm != 'undefined'){
14638             Roo.EventManager.on(window, 'scroll', action, this,
14639                 {buffer: tm == 'number' ? monitorScroll : 50});
14640         }
14641         action.call(this);
14642         return this;
14643     },
14644
14645     /**
14646      * Returns true if the dialog is visible
14647      * @return {Boolean}
14648      */
14649     isVisible : function(){
14650         return this.el.isVisible();
14651     },
14652
14653     // private
14654     animHide : function(callback){
14655         var b = Roo.get(this.animateTarget).getBox();
14656         this.proxy.show();
14657         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14658         this.el.hide();
14659         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14660                     this.hideEl.createDelegate(this, [callback]));
14661     },
14662
14663     /**
14664      * Hides the dialog.
14665      * @param {Function} callback (optional) Function to call when the dialog is hidden
14666      * @return {Roo.BasicDialog} this
14667      */
14668     hide : function(callback){
14669         if (this.fireEvent("beforehide", this) === false){
14670             return;
14671         }
14672         if(this.shadow){
14673             this.shadow.hide();
14674         }
14675         if(this.shim) {
14676           this.shim.hide();
14677         }
14678         // sometimes animateTarget seems to get set.. causing problems...
14679         // this just double checks..
14680         if(this.animateTarget && Roo.get(this.animateTarget)) {
14681            this.animHide(callback);
14682         }else{
14683             this.el.hide();
14684             this.hideEl(callback);
14685         }
14686         return this;
14687     },
14688
14689     // private
14690     hideEl : function(callback){
14691         this.proxy.hide();
14692         if(this.modal){
14693             this.mask.hide();
14694             Roo.get(document.body).removeClass("x-body-masked");
14695         }
14696         this.fireEvent("hide", this);
14697         if(typeof callback == "function"){
14698             callback();
14699         }
14700     },
14701
14702     // private
14703     hideAction : function(){
14704         this.setLeft("-10000px");
14705         this.setTop("-10000px");
14706         this.setStyle("visibility", "hidden");
14707     },
14708
14709     // private
14710     refreshSize : function(){
14711         this.size = this.el.getSize();
14712         this.xy = this.el.getXY();
14713         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14714     },
14715
14716     // private
14717     // z-index is managed by the DialogManager and may be overwritten at any time
14718     setZIndex : function(index){
14719         if(this.modal){
14720             this.mask.setStyle("z-index", index);
14721         }
14722         if(this.shim){
14723             this.shim.setStyle("z-index", ++index);
14724         }
14725         if(this.shadow){
14726             this.shadow.setZIndex(++index);
14727         }
14728         this.el.setStyle("z-index", ++index);
14729         if(this.proxy){
14730             this.proxy.setStyle("z-index", ++index);
14731         }
14732         if(this.resizer){
14733             this.resizer.proxy.setStyle("z-index", ++index);
14734         }
14735
14736         this.lastZIndex = index;
14737     },
14738
14739     /**
14740      * Returns the element for this dialog
14741      * @return {Roo.Element} The underlying dialog Element
14742      */
14743     getEl : function(){
14744         return this.el;
14745     }
14746 });
14747
14748 /**
14749  * @class Roo.DialogManager
14750  * Provides global access to BasicDialogs that have been created and
14751  * support for z-indexing (layering) multiple open dialogs.
14752  */
14753 Roo.DialogManager = function(){
14754     var list = {};
14755     var accessList = [];
14756     var front = null;
14757
14758     // private
14759     var sortDialogs = function(d1, d2){
14760         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14761     };
14762
14763     // private
14764     var orderDialogs = function(){
14765         accessList.sort(sortDialogs);
14766         var seed = Roo.DialogManager.zseed;
14767         for(var i = 0, len = accessList.length; i < len; i++){
14768             var dlg = accessList[i];
14769             if(dlg){
14770                 dlg.setZIndex(seed + (i*10));
14771             }
14772         }
14773     };
14774
14775     return {
14776         /**
14777          * The starting z-index for BasicDialogs (defaults to 9000)
14778          * @type Number The z-index value
14779          */
14780         zseed : 9000,
14781
14782         // private
14783         register : function(dlg){
14784             list[dlg.id] = dlg;
14785             accessList.push(dlg);
14786         },
14787
14788         // private
14789         unregister : function(dlg){
14790             delete list[dlg.id];
14791             var i=0;
14792             var len=0;
14793             if(!accessList.indexOf){
14794                 for(  i = 0, len = accessList.length; i < len; i++){
14795                     if(accessList[i] == dlg){
14796                         accessList.splice(i, 1);
14797                         return;
14798                     }
14799                 }
14800             }else{
14801                  i = accessList.indexOf(dlg);
14802                 if(i != -1){
14803                     accessList.splice(i, 1);
14804                 }
14805             }
14806         },
14807
14808         /**
14809          * Gets a registered dialog by id
14810          * @param {String/Object} id The id of the dialog or a dialog
14811          * @return {Roo.BasicDialog} this
14812          */
14813         get : function(id){
14814             return typeof id == "object" ? id : list[id];
14815         },
14816
14817         /**
14818          * Brings the specified dialog to the front
14819          * @param {String/Object} dlg The id of the dialog or a dialog
14820          * @return {Roo.BasicDialog} this
14821          */
14822         bringToFront : function(dlg){
14823             dlg = this.get(dlg);
14824             if(dlg != front){
14825                 front = dlg;
14826                 dlg._lastAccess = new Date().getTime();
14827                 orderDialogs();
14828             }
14829             return dlg;
14830         },
14831
14832         /**
14833          * Sends the specified dialog to the back
14834          * @param {String/Object} dlg The id of the dialog or a dialog
14835          * @return {Roo.BasicDialog} this
14836          */
14837         sendToBack : function(dlg){
14838             dlg = this.get(dlg);
14839             dlg._lastAccess = -(new Date().getTime());
14840             orderDialogs();
14841             return dlg;
14842         },
14843
14844         /**
14845          * Hides all dialogs
14846          */
14847         hideAll : function(){
14848             for(var id in list){
14849                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14850                     list[id].hide();
14851                 }
14852             }
14853         }
14854     };
14855 }();
14856
14857 /**
14858  * @class Roo.LayoutDialog
14859  * @extends Roo.BasicDialog
14860  * Dialog which provides adjustments for working with a layout in a Dialog.
14861  * Add your necessary layout config options to the dialog's config.<br>
14862  * Example usage (including a nested layout):
14863  * <pre><code>
14864 if(!dialog){
14865     dialog = new Roo.LayoutDialog("download-dlg", {
14866         modal: true,
14867         width:600,
14868         height:450,
14869         shadow:true,
14870         minWidth:500,
14871         minHeight:350,
14872         autoTabs:true,
14873         proxyDrag:true,
14874         // layout config merges with the dialog config
14875         center:{
14876             tabPosition: "top",
14877             alwaysShowTabs: true
14878         }
14879     });
14880     dialog.addKeyListener(27, dialog.hide, dialog);
14881     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14882     dialog.addButton("Build It!", this.getDownload, this);
14883
14884     // we can even add nested layouts
14885     var innerLayout = new Roo.BorderLayout("dl-inner", {
14886         east: {
14887             initialSize: 200,
14888             autoScroll:true,
14889             split:true
14890         },
14891         center: {
14892             autoScroll:true
14893         }
14894     });
14895     innerLayout.beginUpdate();
14896     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14897     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14898     innerLayout.endUpdate(true);
14899
14900     var layout = dialog.getLayout();
14901     layout.beginUpdate();
14902     layout.add("center", new Roo.ContentPanel("standard-panel",
14903                         {title: "Download the Source", fitToFrame:true}));
14904     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14905                {title: "Build your own roo.js"}));
14906     layout.getRegion("center").showPanel(sp);
14907     layout.endUpdate();
14908 }
14909 </code></pre>
14910     * @constructor
14911     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14912     * @param {Object} config configuration options
14913   */
14914 Roo.LayoutDialog = function(el, cfg){
14915     
14916     var config=  cfg;
14917     if (typeof(cfg) == 'undefined') {
14918         config = Roo.apply({}, el);
14919         // not sure why we use documentElement here.. - it should always be body.
14920         // IE7 borks horribly if we use documentElement.
14921         // webkit also does not like documentElement - it creates a body element...
14922         el = Roo.get( document.body || document.documentElement ).createChild();
14923         //config.autoCreate = true;
14924     }
14925     
14926     
14927     config.autoTabs = false;
14928     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14929     this.body.setStyle({overflow:"hidden", position:"relative"});
14930     this.layout = new Roo.BorderLayout(this.body.dom, config);
14931     this.layout.monitorWindowResize = false;
14932     this.el.addClass("x-dlg-auto-layout");
14933     // fix case when center region overwrites center function
14934     this.center = Roo.BasicDialog.prototype.center;
14935     this.on("show", this.layout.layout, this.layout, true);
14936     if (config.items) {
14937         var xitems = config.items;
14938         delete config.items;
14939         Roo.each(xitems, this.addxtype, this);
14940     }
14941     
14942     
14943 };
14944 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14945     /**
14946      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14947      * @deprecated
14948      */
14949     endUpdate : function(){
14950         this.layout.endUpdate();
14951     },
14952
14953     /**
14954      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14955      *  @deprecated
14956      */
14957     beginUpdate : function(){
14958         this.layout.beginUpdate();
14959     },
14960
14961     /**
14962      * Get the BorderLayout for this dialog
14963      * @return {Roo.BorderLayout}
14964      */
14965     getLayout : function(){
14966         return this.layout;
14967     },
14968
14969     showEl : function(){
14970         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14971         if(Roo.isIE7){
14972             this.layout.layout();
14973         }
14974     },
14975
14976     // private
14977     // Use the syncHeightBeforeShow config option to control this automatically
14978     syncBodyHeight : function(){
14979         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14980         if(this.layout){this.layout.layout();}
14981     },
14982     
14983       /**
14984      * Add an xtype element (actually adds to the layout.)
14985      * @return {Object} xdata xtype object data.
14986      */
14987     
14988     addxtype : function(c) {
14989         return this.layout.addxtype(c);
14990     }
14991 });/*
14992  * Based on:
14993  * Ext JS Library 1.1.1
14994  * Copyright(c) 2006-2007, Ext JS, LLC.
14995  *
14996  * Originally Released Under LGPL - original licence link has changed is not relivant.
14997  *
14998  * Fork - LGPL
14999  * <script type="text/javascript">
15000  */
15001  
15002 /**
15003  * @class Roo.MessageBox
15004  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15005  * Example usage:
15006  *<pre><code>
15007 // Basic alert:
15008 Roo.Msg.alert('Status', 'Changes saved successfully.');
15009
15010 // Prompt for user data:
15011 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15012     if (btn == 'ok'){
15013         // process text value...
15014     }
15015 });
15016
15017 // Show a dialog using config options:
15018 Roo.Msg.show({
15019    title:'Save Changes?',
15020    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15021    buttons: Roo.Msg.YESNOCANCEL,
15022    fn: processResult,
15023    animEl: 'elId'
15024 });
15025 </code></pre>
15026  * @singleton
15027  */
15028 Roo.MessageBox = function(){
15029     var dlg, opt, mask, waitTimer;
15030     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15031     var buttons, activeTextEl, bwidth;
15032
15033     // private
15034     var handleButton = function(button){
15035         dlg.hide();
15036         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15037     };
15038
15039     // private
15040     var handleHide = function(){
15041         if(opt && opt.cls){
15042             dlg.el.removeClass(opt.cls);
15043         }
15044         if(waitTimer){
15045             Roo.TaskMgr.stop(waitTimer);
15046             waitTimer = null;
15047         }
15048     };
15049
15050     // private
15051     var updateButtons = function(b){
15052         var width = 0;
15053         if(!b){
15054             buttons["ok"].hide();
15055             buttons["cancel"].hide();
15056             buttons["yes"].hide();
15057             buttons["no"].hide();
15058             dlg.footer.dom.style.display = 'none';
15059             return width;
15060         }
15061         dlg.footer.dom.style.display = '';
15062         for(var k in buttons){
15063             if(typeof buttons[k] != "function"){
15064                 if(b[k]){
15065                     buttons[k].show();
15066                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15067                     width += buttons[k].el.getWidth()+15;
15068                 }else{
15069                     buttons[k].hide();
15070                 }
15071             }
15072         }
15073         return width;
15074     };
15075
15076     // private
15077     var handleEsc = function(d, k, e){
15078         if(opt && opt.closable !== false){
15079             dlg.hide();
15080         }
15081         if(e){
15082             e.stopEvent();
15083         }
15084     };
15085
15086     return {
15087         /**
15088          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15089          * @return {Roo.BasicDialog} The BasicDialog element
15090          */
15091         getDialog : function(){
15092            if(!dlg){
15093                 dlg = new Roo.BasicDialog("x-msg-box", {
15094                     autoCreate : true,
15095                     shadow: true,
15096                     draggable: true,
15097                     resizable:false,
15098                     constraintoviewport:false,
15099                     fixedcenter:true,
15100                     collapsible : false,
15101                     shim:true,
15102                     modal: true,
15103                     width:400, height:100,
15104                     buttonAlign:"center",
15105                     closeClick : function(){
15106                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15107                             handleButton("no");
15108                         }else{
15109                             handleButton("cancel");
15110                         }
15111                     }
15112                 });
15113                 dlg.on("hide", handleHide);
15114                 mask = dlg.mask;
15115                 dlg.addKeyListener(27, handleEsc);
15116                 buttons = {};
15117                 var bt = this.buttonText;
15118                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15119                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15120                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15121                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15122                 bodyEl = dlg.body.createChild({
15123
15124                     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>'
15125                 });
15126                 msgEl = bodyEl.dom.firstChild;
15127                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15128                 textboxEl.enableDisplayMode();
15129                 textboxEl.addKeyListener([10,13], function(){
15130                     if(dlg.isVisible() && opt && opt.buttons){
15131                         if(opt.buttons.ok){
15132                             handleButton("ok");
15133                         }else if(opt.buttons.yes){
15134                             handleButton("yes");
15135                         }
15136                     }
15137                 });
15138                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15139                 textareaEl.enableDisplayMode();
15140                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15141                 progressEl.enableDisplayMode();
15142                 var pf = progressEl.dom.firstChild;
15143                 if (pf) {
15144                     pp = Roo.get(pf.firstChild);
15145                     pp.setHeight(pf.offsetHeight);
15146                 }
15147                 
15148             }
15149             return dlg;
15150         },
15151
15152         /**
15153          * Updates the message box body text
15154          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15155          * the XHTML-compliant non-breaking space character '&amp;#160;')
15156          * @return {Roo.MessageBox} This message box
15157          */
15158         updateText : function(text){
15159             if(!dlg.isVisible() && !opt.width){
15160                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15161             }
15162             msgEl.innerHTML = text || '&#160;';
15163       
15164             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15165             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15166             var w = Math.max(
15167                     Math.min(opt.width || cw , this.maxWidth), 
15168                     Math.max(opt.minWidth || this.minWidth, bwidth)
15169             );
15170             if(opt.prompt){
15171                 activeTextEl.setWidth(w);
15172             }
15173             if(dlg.isVisible()){
15174                 dlg.fixedcenter = false;
15175             }
15176             // to big, make it scroll. = But as usual stupid IE does not support
15177             // !important..
15178             
15179             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15180                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15181                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15182             } else {
15183                 bodyEl.dom.style.height = '';
15184                 bodyEl.dom.style.overflowY = '';
15185             }
15186             if (cw > w) {
15187                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15188             } else {
15189                 bodyEl.dom.style.overflowX = '';
15190             }
15191             
15192             dlg.setContentSize(w, bodyEl.getHeight());
15193             if(dlg.isVisible()){
15194                 dlg.fixedcenter = true;
15195             }
15196             return this;
15197         },
15198
15199         /**
15200          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15201          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15202          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15203          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15204          * @return {Roo.MessageBox} This message box
15205          */
15206         updateProgress : function(value, text){
15207             if(text){
15208                 this.updateText(text);
15209             }
15210             if (pp) { // weird bug on my firefox - for some reason this is not defined
15211                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15212             }
15213             return this;
15214         },        
15215
15216         /**
15217          * Returns true if the message box is currently displayed
15218          * @return {Boolean} True if the message box is visible, else false
15219          */
15220         isVisible : function(){
15221             return dlg && dlg.isVisible();  
15222         },
15223
15224         /**
15225          * Hides the message box if it is displayed
15226          */
15227         hide : function(){
15228             if(this.isVisible()){
15229                 dlg.hide();
15230             }  
15231         },
15232
15233         /**
15234          * Displays a new message box, or reinitializes an existing message box, based on the config options
15235          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15236          * The following config object properties are supported:
15237          * <pre>
15238 Property    Type             Description
15239 ----------  ---------------  ------------------------------------------------------------------------------------
15240 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15241                                    closes (defaults to undefined)
15242 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15243                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15244 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15245                                    progress and wait dialogs will ignore this property and always hide the
15246                                    close button as they can only be closed programmatically.
15247 cls               String           A custom CSS class to apply to the message box element
15248 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15249                                    displayed (defaults to 75)
15250 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15251                                    function will be btn (the name of the button that was clicked, if applicable,
15252                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15253                                    Progress and wait dialogs will ignore this option since they do not respond to
15254                                    user actions and can only be closed programmatically, so any required function
15255                                    should be called by the same code after it closes the dialog.
15256 icon              String           A CSS class that provides a background image to be used as an icon for
15257                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15258 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15259 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15260 modal             Boolean          False to allow user interaction with the page while the message box is
15261                                    displayed (defaults to true)
15262 msg               String           A string that will replace the existing message box body text (defaults
15263                                    to the XHTML-compliant non-breaking space character '&#160;')
15264 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15265 progress          Boolean          True to display a progress bar (defaults to false)
15266 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15267 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15268 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15269 title             String           The title text
15270 value             String           The string value to set into the active textbox element if displayed
15271 wait              Boolean          True to display a progress bar (defaults to false)
15272 width             Number           The width of the dialog in pixels
15273 </pre>
15274          *
15275          * Example usage:
15276          * <pre><code>
15277 Roo.Msg.show({
15278    title: 'Address',
15279    msg: 'Please enter your address:',
15280    width: 300,
15281    buttons: Roo.MessageBox.OKCANCEL,
15282    multiline: true,
15283    fn: saveAddress,
15284    animEl: 'addAddressBtn'
15285 });
15286 </code></pre>
15287          * @param {Object} config Configuration options
15288          * @return {Roo.MessageBox} This message box
15289          */
15290         show : function(options)
15291         {
15292             
15293             // this causes nightmares if you show one dialog after another
15294             // especially on callbacks..
15295              
15296             if(this.isVisible()){
15297                 
15298                 this.hide();
15299                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15300                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15301                 Roo.log("New Dialog Message:" +  options.msg )
15302                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15303                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15304                 
15305             }
15306             var d = this.getDialog();
15307             opt = options;
15308             d.setTitle(opt.title || "&#160;");
15309             d.close.setDisplayed(opt.closable !== false);
15310             activeTextEl = textboxEl;
15311             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15312             if(opt.prompt){
15313                 if(opt.multiline){
15314                     textboxEl.hide();
15315                     textareaEl.show();
15316                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15317                         opt.multiline : this.defaultTextHeight);
15318                     activeTextEl = textareaEl;
15319                 }else{
15320                     textboxEl.show();
15321                     textareaEl.hide();
15322                 }
15323             }else{
15324                 textboxEl.hide();
15325                 textareaEl.hide();
15326             }
15327             progressEl.setDisplayed(opt.progress === true);
15328             this.updateProgress(0);
15329             activeTextEl.dom.value = opt.value || "";
15330             if(opt.prompt){
15331                 dlg.setDefaultButton(activeTextEl);
15332             }else{
15333                 var bs = opt.buttons;
15334                 var db = null;
15335                 if(bs && bs.ok){
15336                     db = buttons["ok"];
15337                 }else if(bs && bs.yes){
15338                     db = buttons["yes"];
15339                 }
15340                 dlg.setDefaultButton(db);
15341             }
15342             bwidth = updateButtons(opt.buttons);
15343             this.updateText(opt.msg);
15344             if(opt.cls){
15345                 d.el.addClass(opt.cls);
15346             }
15347             d.proxyDrag = opt.proxyDrag === true;
15348             d.modal = opt.modal !== false;
15349             d.mask = opt.modal !== false ? mask : false;
15350             if(!d.isVisible()){
15351                 // force it to the end of the z-index stack so it gets a cursor in FF
15352                 document.body.appendChild(dlg.el.dom);
15353                 d.animateTarget = null;
15354                 d.show(options.animEl);
15355             }
15356             return this;
15357         },
15358
15359         /**
15360          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15361          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15362          * and closing the message box when the process is complete.
15363          * @param {String} title The title bar text
15364          * @param {String} msg The message box body text
15365          * @return {Roo.MessageBox} This message box
15366          */
15367         progress : function(title, msg){
15368             this.show({
15369                 title : title,
15370                 msg : msg,
15371                 buttons: false,
15372                 progress:true,
15373                 closable:false,
15374                 minWidth: this.minProgressWidth,
15375                 modal : true
15376             });
15377             return this;
15378         },
15379
15380         /**
15381          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15382          * If a callback function is passed it will be called after the user clicks the button, and the
15383          * id of the button that was clicked will be passed as the only parameter to the callback
15384          * (could also be the top-right close button).
15385          * @param {String} title The title bar text
15386          * @param {String} msg The message box body text
15387          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15388          * @param {Object} scope (optional) The scope of the callback function
15389          * @return {Roo.MessageBox} This message box
15390          */
15391         alert : function(title, msg, fn, scope){
15392             this.show({
15393                 title : title,
15394                 msg : msg,
15395                 buttons: this.OK,
15396                 fn: fn,
15397                 scope : scope,
15398                 modal : true
15399             });
15400             return this;
15401         },
15402
15403         /**
15404          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15405          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15406          * You are responsible for closing the message box when the process is complete.
15407          * @param {String} msg The message box body text
15408          * @param {String} title (optional) The title bar text
15409          * @return {Roo.MessageBox} This message box
15410          */
15411         wait : function(msg, title){
15412             this.show({
15413                 title : title,
15414                 msg : msg,
15415                 buttons: false,
15416                 closable:false,
15417                 progress:true,
15418                 modal:true,
15419                 width:300,
15420                 wait:true
15421             });
15422             waitTimer = Roo.TaskMgr.start({
15423                 run: function(i){
15424                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15425                 },
15426                 interval: 1000
15427             });
15428             return this;
15429         },
15430
15431         /**
15432          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15433          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15434          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15435          * @param {String} title The title bar text
15436          * @param {String} msg The message box body text
15437          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15438          * @param {Object} scope (optional) The scope of the callback function
15439          * @return {Roo.MessageBox} This message box
15440          */
15441         confirm : function(title, msg, fn, scope){
15442             this.show({
15443                 title : title,
15444                 msg : msg,
15445                 buttons: this.YESNO,
15446                 fn: fn,
15447                 scope : scope,
15448                 modal : true
15449             });
15450             return this;
15451         },
15452
15453         /**
15454          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15455          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15456          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15457          * (could also be the top-right close button) and the text that was entered will be passed as the two
15458          * parameters to the callback.
15459          * @param {String} title The title bar text
15460          * @param {String} msg The message box body text
15461          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15462          * @param {Object} scope (optional) The scope of the callback function
15463          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15464          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15465          * @return {Roo.MessageBox} This message box
15466          */
15467         prompt : function(title, msg, fn, scope, multiline){
15468             this.show({
15469                 title : title,
15470                 msg : msg,
15471                 buttons: this.OKCANCEL,
15472                 fn: fn,
15473                 minWidth:250,
15474                 scope : scope,
15475                 prompt:true,
15476                 multiline: multiline,
15477                 modal : true
15478             });
15479             return this;
15480         },
15481
15482         /**
15483          * Button config that displays a single OK button
15484          * @type Object
15485          */
15486         OK : {ok:true},
15487         /**
15488          * Button config that displays Yes and No buttons
15489          * @type Object
15490          */
15491         YESNO : {yes:true, no:true},
15492         /**
15493          * Button config that displays OK and Cancel buttons
15494          * @type Object
15495          */
15496         OKCANCEL : {ok:true, cancel:true},
15497         /**
15498          * Button config that displays Yes, No and Cancel buttons
15499          * @type Object
15500          */
15501         YESNOCANCEL : {yes:true, no:true, cancel:true},
15502
15503         /**
15504          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15505          * @type Number
15506          */
15507         defaultTextHeight : 75,
15508         /**
15509          * The maximum width in pixels of the message box (defaults to 600)
15510          * @type Number
15511          */
15512         maxWidth : 600,
15513         /**
15514          * The minimum width in pixels of the message box (defaults to 100)
15515          * @type Number
15516          */
15517         minWidth : 100,
15518         /**
15519          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15520          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15521          * @type Number
15522          */
15523         minProgressWidth : 250,
15524         /**
15525          * An object containing the default button text strings that can be overriden for localized language support.
15526          * Supported properties are: ok, cancel, yes and no.
15527          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15528          * @type Object
15529          */
15530         buttonText : {
15531             ok : "OK",
15532             cancel : "Cancel",
15533             yes : "Yes",
15534             no : "No"
15535         }
15536     };
15537 }();
15538
15539 /**
15540  * Shorthand for {@link Roo.MessageBox}
15541  */
15542 Roo.Msg = Roo.MessageBox;/*
15543  * Based on:
15544  * Ext JS Library 1.1.1
15545  * Copyright(c) 2006-2007, Ext JS, LLC.
15546  *
15547  * Originally Released Under LGPL - original licence link has changed is not relivant.
15548  *
15549  * Fork - LGPL
15550  * <script type="text/javascript">
15551  */
15552 /**
15553  * @class Roo.QuickTips
15554  * Provides attractive and customizable tooltips for any element.
15555  * @singleton
15556  */
15557 Roo.QuickTips = function(){
15558     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15559     var ce, bd, xy, dd;
15560     var visible = false, disabled = true, inited = false;
15561     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15562     
15563     var onOver = function(e){
15564         if(disabled){
15565             return;
15566         }
15567         var t = e.getTarget();
15568         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15569             return;
15570         }
15571         if(ce && t == ce.el){
15572             clearTimeout(hideProc);
15573             return;
15574         }
15575         if(t && tagEls[t.id]){
15576             tagEls[t.id].el = t;
15577             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15578             return;
15579         }
15580         var ttp, et = Roo.fly(t);
15581         var ns = cfg.namespace;
15582         if(tm.interceptTitles && t.title){
15583             ttp = t.title;
15584             t.qtip = ttp;
15585             t.removeAttribute("title");
15586             e.preventDefault();
15587         }else{
15588             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
15589         }
15590         if(ttp){
15591             showProc = show.defer(tm.showDelay, tm, [{
15592                 el: t, 
15593                 text: ttp, 
15594                 width: et.getAttributeNS(ns, cfg.width),
15595                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15596                 title: et.getAttributeNS(ns, cfg.title),
15597                     cls: et.getAttributeNS(ns, cfg.cls)
15598             }]);
15599         }
15600     };
15601     
15602     var onOut = function(e){
15603         clearTimeout(showProc);
15604         var t = e.getTarget();
15605         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15606             hideProc = setTimeout(hide, tm.hideDelay);
15607         }
15608     };
15609     
15610     var onMove = function(e){
15611         if(disabled){
15612             return;
15613         }
15614         xy = e.getXY();
15615         xy[1] += 18;
15616         if(tm.trackMouse && ce){
15617             el.setXY(xy);
15618         }
15619     };
15620     
15621     var onDown = function(e){
15622         clearTimeout(showProc);
15623         clearTimeout(hideProc);
15624         if(!e.within(el)){
15625             if(tm.hideOnClick){
15626                 hide();
15627                 tm.disable();
15628                 tm.enable.defer(100, tm);
15629             }
15630         }
15631     };
15632     
15633     var getPad = function(){
15634         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15635     };
15636
15637     var show = function(o){
15638         if(disabled){
15639             return;
15640         }
15641         clearTimeout(dismissProc);
15642         ce = o;
15643         if(removeCls){ // in case manually hidden
15644             el.removeClass(removeCls);
15645             removeCls = null;
15646         }
15647         if(ce.cls){
15648             el.addClass(ce.cls);
15649             removeCls = ce.cls;
15650         }
15651         if(ce.title){
15652             tipTitle.update(ce.title);
15653             tipTitle.show();
15654         }else{
15655             tipTitle.update('');
15656             tipTitle.hide();
15657         }
15658         el.dom.style.width  = tm.maxWidth+'px';
15659         //tipBody.dom.style.width = '';
15660         tipBodyText.update(o.text);
15661         var p = getPad(), w = ce.width;
15662         if(!w){
15663             var td = tipBodyText.dom;
15664             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15665             if(aw > tm.maxWidth){
15666                 w = tm.maxWidth;
15667             }else if(aw < tm.minWidth){
15668                 w = tm.minWidth;
15669             }else{
15670                 w = aw;
15671             }
15672         }
15673         //tipBody.setWidth(w);
15674         el.setWidth(parseInt(w, 10) + p);
15675         if(ce.autoHide === false){
15676             close.setDisplayed(true);
15677             if(dd){
15678                 dd.unlock();
15679             }
15680         }else{
15681             close.setDisplayed(false);
15682             if(dd){
15683                 dd.lock();
15684             }
15685         }
15686         if(xy){
15687             el.avoidY = xy[1]-18;
15688             el.setXY(xy);
15689         }
15690         if(tm.animate){
15691             el.setOpacity(.1);
15692             el.setStyle("visibility", "visible");
15693             el.fadeIn({callback: afterShow});
15694         }else{
15695             afterShow();
15696         }
15697     };
15698     
15699     var afterShow = function(){
15700         if(ce){
15701             el.show();
15702             esc.enable();
15703             if(tm.autoDismiss && ce.autoHide !== false){
15704                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15705             }
15706         }
15707     };
15708     
15709     var hide = function(noanim){
15710         clearTimeout(dismissProc);
15711         clearTimeout(hideProc);
15712         ce = null;
15713         if(el.isVisible()){
15714             esc.disable();
15715             if(noanim !== true && tm.animate){
15716                 el.fadeOut({callback: afterHide});
15717             }else{
15718                 afterHide();
15719             } 
15720         }
15721     };
15722     
15723     var afterHide = function(){
15724         el.hide();
15725         if(removeCls){
15726             el.removeClass(removeCls);
15727             removeCls = null;
15728         }
15729     };
15730     
15731     return {
15732         /**
15733         * @cfg {Number} minWidth
15734         * The minimum width of the quick tip (defaults to 40)
15735         */
15736        minWidth : 40,
15737         /**
15738         * @cfg {Number} maxWidth
15739         * The maximum width of the quick tip (defaults to 300)
15740         */
15741        maxWidth : 300,
15742         /**
15743         * @cfg {Boolean} interceptTitles
15744         * True to automatically use the element's DOM title value if available (defaults to false)
15745         */
15746        interceptTitles : false,
15747         /**
15748         * @cfg {Boolean} trackMouse
15749         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15750         */
15751        trackMouse : false,
15752         /**
15753         * @cfg {Boolean} hideOnClick
15754         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15755         */
15756        hideOnClick : true,
15757         /**
15758         * @cfg {Number} showDelay
15759         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15760         */
15761        showDelay : 500,
15762         /**
15763         * @cfg {Number} hideDelay
15764         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15765         */
15766        hideDelay : 200,
15767         /**
15768         * @cfg {Boolean} autoHide
15769         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15770         * Used in conjunction with hideDelay.
15771         */
15772        autoHide : true,
15773         /**
15774         * @cfg {Boolean}
15775         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15776         * (defaults to true).  Used in conjunction with autoDismissDelay.
15777         */
15778        autoDismiss : true,
15779         /**
15780         * @cfg {Number}
15781         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15782         */
15783        autoDismissDelay : 5000,
15784        /**
15785         * @cfg {Boolean} animate
15786         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15787         */
15788        animate : false,
15789
15790        /**
15791         * @cfg {String} title
15792         * Title text to display (defaults to '').  This can be any valid HTML markup.
15793         */
15794         title: '',
15795        /**
15796         * @cfg {String} text
15797         * Body text to display (defaults to '').  This can be any valid HTML markup.
15798         */
15799         text : '',
15800        /**
15801         * @cfg {String} cls
15802         * A CSS class to apply to the base quick tip element (defaults to '').
15803         */
15804         cls : '',
15805        /**
15806         * @cfg {Number} width
15807         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15808         * minWidth or maxWidth.
15809         */
15810         width : null,
15811
15812     /**
15813      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15814      * or display QuickTips in a page.
15815      */
15816        init : function(){
15817           tm = Roo.QuickTips;
15818           cfg = tm.tagConfig;
15819           if(!inited){
15820               if(!Roo.isReady){ // allow calling of init() before onReady
15821                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15822                   return;
15823               }
15824               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15825               el.fxDefaults = {stopFx: true};
15826               // maximum custom styling
15827               //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>');
15828               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>');              
15829               tipTitle = el.child('h3');
15830               tipTitle.enableDisplayMode("block");
15831               tipBody = el.child('div.x-tip-bd');
15832               tipBodyText = el.child('div.x-tip-bd-inner');
15833               //bdLeft = el.child('div.x-tip-bd-left');
15834               //bdRight = el.child('div.x-tip-bd-right');
15835               close = el.child('div.x-tip-close');
15836               close.enableDisplayMode("block");
15837               close.on("click", hide);
15838               var d = Roo.get(document);
15839               d.on("mousedown", onDown);
15840               d.on("mouseover", onOver);
15841               d.on("mouseout", onOut);
15842               d.on("mousemove", onMove);
15843               esc = d.addKeyListener(27, hide);
15844               esc.disable();
15845               if(Roo.dd.DD){
15846                   dd = el.initDD("default", null, {
15847                       onDrag : function(){
15848                           el.sync();  
15849                       }
15850                   });
15851                   dd.setHandleElId(tipTitle.id);
15852                   dd.lock();
15853               }
15854               inited = true;
15855           }
15856           this.enable(); 
15857        },
15858
15859     /**
15860      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15861      * are supported:
15862      * <pre>
15863 Property    Type                   Description
15864 ----------  ---------------------  ------------------------------------------------------------------------
15865 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15866      * </ul>
15867      * @param {Object} config The config object
15868      */
15869        register : function(config){
15870            var cs = config instanceof Array ? config : arguments;
15871            for(var i = 0, len = cs.length; i < len; i++) {
15872                var c = cs[i];
15873                var target = c.target;
15874                if(target){
15875                    if(target instanceof Array){
15876                        for(var j = 0, jlen = target.length; j < jlen; j++){
15877                            tagEls[target[j]] = c;
15878                        }
15879                    }else{
15880                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15881                    }
15882                }
15883            }
15884        },
15885
15886     /**
15887      * Removes this quick tip from its element and destroys it.
15888      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15889      */
15890        unregister : function(el){
15891            delete tagEls[Roo.id(el)];
15892        },
15893
15894     /**
15895      * Enable this quick tip.
15896      */
15897        enable : function(){
15898            if(inited && disabled){
15899                locks.pop();
15900                if(locks.length < 1){
15901                    disabled = false;
15902                }
15903            }
15904        },
15905
15906     /**
15907      * Disable this quick tip.
15908      */
15909        disable : function(){
15910           disabled = true;
15911           clearTimeout(showProc);
15912           clearTimeout(hideProc);
15913           clearTimeout(dismissProc);
15914           if(ce){
15915               hide(true);
15916           }
15917           locks.push(1);
15918        },
15919
15920     /**
15921      * Returns true if the quick tip is enabled, else false.
15922      */
15923        isEnabled : function(){
15924             return !disabled;
15925        },
15926
15927         // private
15928        tagConfig : {
15929            namespace : "roo", // was ext?? this may break..
15930            alt_namespace : "ext",
15931            attribute : "qtip",
15932            width : "width",
15933            target : "target",
15934            title : "qtitle",
15935            hide : "hide",
15936            cls : "qclass"
15937        }
15938    };
15939 }();
15940
15941 // backwards compat
15942 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15943  * Based on:
15944  * Ext JS Library 1.1.1
15945  * Copyright(c) 2006-2007, Ext JS, LLC.
15946  *
15947  * Originally Released Under LGPL - original licence link has changed is not relivant.
15948  *
15949  * Fork - LGPL
15950  * <script type="text/javascript">
15951  */
15952  
15953
15954 /**
15955  * @class Roo.tree.TreePanel
15956  * @extends Roo.data.Tree
15957
15958  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15959  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15960  * @cfg {Boolean} enableDD true to enable drag and drop
15961  * @cfg {Boolean} enableDrag true to enable just drag
15962  * @cfg {Boolean} enableDrop true to enable just drop
15963  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15964  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15965  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15966  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15967  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15968  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15969  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15970  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15971  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15972  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15973  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15974  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15975  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15976  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15977  * @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>
15978  * @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>
15979  * 
15980  * @constructor
15981  * @param {String/HTMLElement/Element} el The container element
15982  * @param {Object} config
15983  */
15984 Roo.tree.TreePanel = function(el, config){
15985     var root = false;
15986     var loader = false;
15987     if (config.root) {
15988         root = config.root;
15989         delete config.root;
15990     }
15991     if (config.loader) {
15992         loader = config.loader;
15993         delete config.loader;
15994     }
15995     
15996     Roo.apply(this, config);
15997     Roo.tree.TreePanel.superclass.constructor.call(this);
15998     this.el = Roo.get(el);
15999     this.el.addClass('x-tree');
16000     //console.log(root);
16001     if (root) {
16002         this.setRootNode( Roo.factory(root, Roo.tree));
16003     }
16004     if (loader) {
16005         this.loader = Roo.factory(loader, Roo.tree);
16006     }
16007    /**
16008     * Read-only. The id of the container element becomes this TreePanel's id.
16009     */
16010     this.id = this.el.id;
16011     this.addEvents({
16012         /**
16013         * @event beforeload
16014         * Fires before a node is loaded, return false to cancel
16015         * @param {Node} node The node being loaded
16016         */
16017         "beforeload" : true,
16018         /**
16019         * @event load
16020         * Fires when a node is loaded
16021         * @param {Node} node The node that was loaded
16022         */
16023         "load" : true,
16024         /**
16025         * @event textchange
16026         * Fires when the text for a node is changed
16027         * @param {Node} node The node
16028         * @param {String} text The new text
16029         * @param {String} oldText The old text
16030         */
16031         "textchange" : true,
16032         /**
16033         * @event beforeexpand
16034         * Fires before a node is expanded, return false to cancel.
16035         * @param {Node} node The node
16036         * @param {Boolean} deep
16037         * @param {Boolean} anim
16038         */
16039         "beforeexpand" : true,
16040         /**
16041         * @event beforecollapse
16042         * Fires before a node is collapsed, return false to cancel.
16043         * @param {Node} node The node
16044         * @param {Boolean} deep
16045         * @param {Boolean} anim
16046         */
16047         "beforecollapse" : true,
16048         /**
16049         * @event expand
16050         * Fires when a node is expanded
16051         * @param {Node} node The node
16052         */
16053         "expand" : true,
16054         /**
16055         * @event disabledchange
16056         * Fires when the disabled status of a node changes
16057         * @param {Node} node The node
16058         * @param {Boolean} disabled
16059         */
16060         "disabledchange" : true,
16061         /**
16062         * @event collapse
16063         * Fires when a node is collapsed
16064         * @param {Node} node The node
16065         */
16066         "collapse" : true,
16067         /**
16068         * @event beforeclick
16069         * Fires before click processing on a node. Return false to cancel the default action.
16070         * @param {Node} node The node
16071         * @param {Roo.EventObject} e The event object
16072         */
16073         "beforeclick":true,
16074         /**
16075         * @event checkchange
16076         * Fires when a node with a checkbox's checked property changes
16077         * @param {Node} this This node
16078         * @param {Boolean} checked
16079         */
16080         "checkchange":true,
16081         /**
16082         * @event click
16083         * Fires when a node is clicked
16084         * @param {Node} node The node
16085         * @param {Roo.EventObject} e The event object
16086         */
16087         "click":true,
16088         /**
16089         * @event dblclick
16090         * Fires when a node is double clicked
16091         * @param {Node} node The node
16092         * @param {Roo.EventObject} e The event object
16093         */
16094         "dblclick":true,
16095         /**
16096         * @event contextmenu
16097         * Fires when a node is right clicked
16098         * @param {Node} node The node
16099         * @param {Roo.EventObject} e The event object
16100         */
16101         "contextmenu":true,
16102         /**
16103         * @event beforechildrenrendered
16104         * Fires right before the child nodes for a node are rendered
16105         * @param {Node} node The node
16106         */
16107         "beforechildrenrendered":true,
16108         /**
16109         * @event startdrag
16110         * Fires when a node starts being dragged
16111         * @param {Roo.tree.TreePanel} this
16112         * @param {Roo.tree.TreeNode} node
16113         * @param {event} e The raw browser event
16114         */ 
16115        "startdrag" : true,
16116        /**
16117         * @event enddrag
16118         * Fires when a drag operation is complete
16119         * @param {Roo.tree.TreePanel} this
16120         * @param {Roo.tree.TreeNode} node
16121         * @param {event} e The raw browser event
16122         */
16123        "enddrag" : true,
16124        /**
16125         * @event dragdrop
16126         * Fires when a dragged node is dropped on a valid DD target
16127         * @param {Roo.tree.TreePanel} this
16128         * @param {Roo.tree.TreeNode} node
16129         * @param {DD} dd The dd it was dropped on
16130         * @param {event} e The raw browser event
16131         */
16132        "dragdrop" : true,
16133        /**
16134         * @event beforenodedrop
16135         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16136         * passed to handlers has the following properties:<br />
16137         * <ul style="padding:5px;padding-left:16px;">
16138         * <li>tree - The TreePanel</li>
16139         * <li>target - The node being targeted for the drop</li>
16140         * <li>data - The drag data from the drag source</li>
16141         * <li>point - The point of the drop - append, above or below</li>
16142         * <li>source - The drag source</li>
16143         * <li>rawEvent - Raw mouse event</li>
16144         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16145         * to be inserted by setting them on this object.</li>
16146         * <li>cancel - Set this to true to cancel the drop.</li>
16147         * </ul>
16148         * @param {Object} dropEvent
16149         */
16150        "beforenodedrop" : true,
16151        /**
16152         * @event nodedrop
16153         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16154         * passed to handlers has the following properties:<br />
16155         * <ul style="padding:5px;padding-left:16px;">
16156         * <li>tree - The TreePanel</li>
16157         * <li>target - The node being targeted for the drop</li>
16158         * <li>data - The drag data from the drag source</li>
16159         * <li>point - The point of the drop - append, above or below</li>
16160         * <li>source - The drag source</li>
16161         * <li>rawEvent - Raw mouse event</li>
16162         * <li>dropNode - Dropped node(s).</li>
16163         * </ul>
16164         * @param {Object} dropEvent
16165         */
16166        "nodedrop" : true,
16167         /**
16168         * @event nodedragover
16169         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16170         * passed to handlers has the following properties:<br />
16171         * <ul style="padding:5px;padding-left:16px;">
16172         * <li>tree - The TreePanel</li>
16173         * <li>target - The node being targeted for the drop</li>
16174         * <li>data - The drag data from the drag source</li>
16175         * <li>point - The point of the drop - append, above or below</li>
16176         * <li>source - The drag source</li>
16177         * <li>rawEvent - Raw mouse event</li>
16178         * <li>dropNode - Drop node(s) provided by the source.</li>
16179         * <li>cancel - Set this to true to signal drop not allowed.</li>
16180         * </ul>
16181         * @param {Object} dragOverEvent
16182         */
16183        "nodedragover" : true
16184         
16185     });
16186     if(this.singleExpand){
16187        this.on("beforeexpand", this.restrictExpand, this);
16188     }
16189     if (this.editor) {
16190         this.editor.tree = this;
16191         this.editor = Roo.factory(this.editor, Roo.tree);
16192     }
16193     
16194     if (this.selModel) {
16195         this.selModel = Roo.factory(this.selModel, Roo.tree);
16196     }
16197    
16198 };
16199 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16200     rootVisible : true,
16201     animate: Roo.enableFx,
16202     lines : true,
16203     enableDD : false,
16204     hlDrop : Roo.enableFx,
16205   
16206     renderer: false,
16207     
16208     rendererTip: false,
16209     // private
16210     restrictExpand : function(node){
16211         var p = node.parentNode;
16212         if(p){
16213             if(p.expandedChild && p.expandedChild.parentNode == p){
16214                 p.expandedChild.collapse();
16215             }
16216             p.expandedChild = node;
16217         }
16218     },
16219
16220     // private override
16221     setRootNode : function(node){
16222         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16223         if(!this.rootVisible){
16224             node.ui = new Roo.tree.RootTreeNodeUI(node);
16225         }
16226         return node;
16227     },
16228
16229     /**
16230      * Returns the container element for this TreePanel
16231      */
16232     getEl : function(){
16233         return this.el;
16234     },
16235
16236     /**
16237      * Returns the default TreeLoader for this TreePanel
16238      */
16239     getLoader : function(){
16240         return this.loader;
16241     },
16242
16243     /**
16244      * Expand all nodes
16245      */
16246     expandAll : function(){
16247         this.root.expand(true);
16248     },
16249
16250     /**
16251      * Collapse all nodes
16252      */
16253     collapseAll : function(){
16254         this.root.collapse(true);
16255     },
16256
16257     /**
16258      * Returns the selection model used by this TreePanel
16259      */
16260     getSelectionModel : function(){
16261         if(!this.selModel){
16262             this.selModel = new Roo.tree.DefaultSelectionModel();
16263         }
16264         return this.selModel;
16265     },
16266
16267     /**
16268      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16269      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16270      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16271      * @return {Array}
16272      */
16273     getChecked : function(a, startNode){
16274         startNode = startNode || this.root;
16275         var r = [];
16276         var f = function(){
16277             if(this.attributes.checked){
16278                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16279             }
16280         }
16281         startNode.cascade(f);
16282         return r;
16283     },
16284
16285     /**
16286      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16287      * @param {String} path
16288      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16289      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16290      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16291      */
16292     expandPath : function(path, attr, callback){
16293         attr = attr || "id";
16294         var keys = path.split(this.pathSeparator);
16295         var curNode = this.root;
16296         if(curNode.attributes[attr] != keys[1]){ // invalid root
16297             if(callback){
16298                 callback(false, null);
16299             }
16300             return;
16301         }
16302         var index = 1;
16303         var f = function(){
16304             if(++index == keys.length){
16305                 if(callback){
16306                     callback(true, curNode);
16307                 }
16308                 return;
16309             }
16310             var c = curNode.findChild(attr, keys[index]);
16311             if(!c){
16312                 if(callback){
16313                     callback(false, curNode);
16314                 }
16315                 return;
16316             }
16317             curNode = c;
16318             c.expand(false, false, f);
16319         };
16320         curNode.expand(false, false, f);
16321     },
16322
16323     /**
16324      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16325      * @param {String} path
16326      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16327      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16328      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16329      */
16330     selectPath : function(path, attr, callback){
16331         attr = attr || "id";
16332         var keys = path.split(this.pathSeparator);
16333         var v = keys.pop();
16334         if(keys.length > 0){
16335             var f = function(success, node){
16336                 if(success && node){
16337                     var n = node.findChild(attr, v);
16338                     if(n){
16339                         n.select();
16340                         if(callback){
16341                             callback(true, n);
16342                         }
16343                     }else if(callback){
16344                         callback(false, n);
16345                     }
16346                 }else{
16347                     if(callback){
16348                         callback(false, n);
16349                     }
16350                 }
16351             };
16352             this.expandPath(keys.join(this.pathSeparator), attr, f);
16353         }else{
16354             this.root.select();
16355             if(callback){
16356                 callback(true, this.root);
16357             }
16358         }
16359     },
16360
16361     getTreeEl : function(){
16362         return this.el;
16363     },
16364
16365     /**
16366      * Trigger rendering of this TreePanel
16367      */
16368     render : function(){
16369         if (this.innerCt) {
16370             return this; // stop it rendering more than once!!
16371         }
16372         
16373         this.innerCt = this.el.createChild({tag:"ul",
16374                cls:"x-tree-root-ct " +
16375                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16376
16377         if(this.containerScroll){
16378             Roo.dd.ScrollManager.register(this.el);
16379         }
16380         if((this.enableDD || this.enableDrop) && !this.dropZone){
16381            /**
16382             * The dropZone used by this tree if drop is enabled
16383             * @type Roo.tree.TreeDropZone
16384             */
16385              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16386                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16387            });
16388         }
16389         if((this.enableDD || this.enableDrag) && !this.dragZone){
16390            /**
16391             * The dragZone used by this tree if drag is enabled
16392             * @type Roo.tree.TreeDragZone
16393             */
16394             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16395                ddGroup: this.ddGroup || "TreeDD",
16396                scroll: this.ddScroll
16397            });
16398         }
16399         this.getSelectionModel().init(this);
16400         if (!this.root) {
16401             Roo.log("ROOT not set in tree");
16402             return this;
16403         }
16404         this.root.render();
16405         if(!this.rootVisible){
16406             this.root.renderChildren();
16407         }
16408         return this;
16409     }
16410 });/*
16411  * Based on:
16412  * Ext JS Library 1.1.1
16413  * Copyright(c) 2006-2007, Ext JS, LLC.
16414  *
16415  * Originally Released Under LGPL - original licence link has changed is not relivant.
16416  *
16417  * Fork - LGPL
16418  * <script type="text/javascript">
16419  */
16420  
16421
16422 /**
16423  * @class Roo.tree.DefaultSelectionModel
16424  * @extends Roo.util.Observable
16425  * The default single selection for a TreePanel.
16426  * @param {Object} cfg Configuration
16427  */
16428 Roo.tree.DefaultSelectionModel = function(cfg){
16429    this.selNode = null;
16430    
16431    
16432    
16433    this.addEvents({
16434        /**
16435         * @event selectionchange
16436         * Fires when the selected node changes
16437         * @param {DefaultSelectionModel} this
16438         * @param {TreeNode} node the new selection
16439         */
16440        "selectionchange" : true,
16441
16442        /**
16443         * @event beforeselect
16444         * Fires before the selected node changes, return false to cancel the change
16445         * @param {DefaultSelectionModel} this
16446         * @param {TreeNode} node the new selection
16447         * @param {TreeNode} node the old selection
16448         */
16449        "beforeselect" : true
16450    });
16451    
16452     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16453 };
16454
16455 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16456     init : function(tree){
16457         this.tree = tree;
16458         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16459         tree.on("click", this.onNodeClick, this);
16460     },
16461     
16462     onNodeClick : function(node, e){
16463         if (e.ctrlKey && this.selNode == node)  {
16464             this.unselect(node);
16465             return;
16466         }
16467         this.select(node);
16468     },
16469     
16470     /**
16471      * Select a node.
16472      * @param {TreeNode} node The node to select
16473      * @return {TreeNode} The selected node
16474      */
16475     select : function(node){
16476         var last = this.selNode;
16477         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16478             if(last){
16479                 last.ui.onSelectedChange(false);
16480             }
16481             this.selNode = node;
16482             node.ui.onSelectedChange(true);
16483             this.fireEvent("selectionchange", this, node, last);
16484         }
16485         return node;
16486     },
16487     
16488     /**
16489      * Deselect a node.
16490      * @param {TreeNode} node The node to unselect
16491      */
16492     unselect : function(node){
16493         if(this.selNode == node){
16494             this.clearSelections();
16495         }    
16496     },
16497     
16498     /**
16499      * Clear all selections
16500      */
16501     clearSelections : function(){
16502         var n = this.selNode;
16503         if(n){
16504             n.ui.onSelectedChange(false);
16505             this.selNode = null;
16506             this.fireEvent("selectionchange", this, null);
16507         }
16508         return n;
16509     },
16510     
16511     /**
16512      * Get the selected node
16513      * @return {TreeNode} The selected node
16514      */
16515     getSelectedNode : function(){
16516         return this.selNode;    
16517     },
16518     
16519     /**
16520      * Returns true if the node is selected
16521      * @param {TreeNode} node The node to check
16522      * @return {Boolean}
16523      */
16524     isSelected : function(node){
16525         return this.selNode == node;  
16526     },
16527
16528     /**
16529      * Selects the node above the selected node in the tree, intelligently walking the nodes
16530      * @return TreeNode The new selection
16531      */
16532     selectPrevious : function(){
16533         var s = this.selNode || this.lastSelNode;
16534         if(!s){
16535             return null;
16536         }
16537         var ps = s.previousSibling;
16538         if(ps){
16539             if(!ps.isExpanded() || ps.childNodes.length < 1){
16540                 return this.select(ps);
16541             } else{
16542                 var lc = ps.lastChild;
16543                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16544                     lc = lc.lastChild;
16545                 }
16546                 return this.select(lc);
16547             }
16548         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16549             return this.select(s.parentNode);
16550         }
16551         return null;
16552     },
16553
16554     /**
16555      * Selects the node above the selected node in the tree, intelligently walking the nodes
16556      * @return TreeNode The new selection
16557      */
16558     selectNext : function(){
16559         var s = this.selNode || this.lastSelNode;
16560         if(!s){
16561             return null;
16562         }
16563         if(s.firstChild && s.isExpanded()){
16564              return this.select(s.firstChild);
16565          }else if(s.nextSibling){
16566              return this.select(s.nextSibling);
16567          }else if(s.parentNode){
16568             var newS = null;
16569             s.parentNode.bubble(function(){
16570                 if(this.nextSibling){
16571                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16572                     return false;
16573                 }
16574             });
16575             return newS;
16576          }
16577         return null;
16578     },
16579
16580     onKeyDown : function(e){
16581         var s = this.selNode || this.lastSelNode;
16582         // undesirable, but required
16583         var sm = this;
16584         if(!s){
16585             return;
16586         }
16587         var k = e.getKey();
16588         switch(k){
16589              case e.DOWN:
16590                  e.stopEvent();
16591                  this.selectNext();
16592              break;
16593              case e.UP:
16594                  e.stopEvent();
16595                  this.selectPrevious();
16596              break;
16597              case e.RIGHT:
16598                  e.preventDefault();
16599                  if(s.hasChildNodes()){
16600                      if(!s.isExpanded()){
16601                          s.expand();
16602                      }else if(s.firstChild){
16603                          this.select(s.firstChild, e);
16604                      }
16605                  }
16606              break;
16607              case e.LEFT:
16608                  e.preventDefault();
16609                  if(s.hasChildNodes() && s.isExpanded()){
16610                      s.collapse();
16611                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16612                      this.select(s.parentNode, e);
16613                  }
16614              break;
16615         };
16616     }
16617 });
16618
16619 /**
16620  * @class Roo.tree.MultiSelectionModel
16621  * @extends Roo.util.Observable
16622  * Multi selection for a TreePanel.
16623  * @param {Object} cfg Configuration
16624  */
16625 Roo.tree.MultiSelectionModel = function(){
16626    this.selNodes = [];
16627    this.selMap = {};
16628    this.addEvents({
16629        /**
16630         * @event selectionchange
16631         * Fires when the selected nodes change
16632         * @param {MultiSelectionModel} this
16633         * @param {Array} nodes Array of the selected nodes
16634         */
16635        "selectionchange" : true
16636    });
16637    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16638    
16639 };
16640
16641 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16642     init : function(tree){
16643         this.tree = tree;
16644         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16645         tree.on("click", this.onNodeClick, this);
16646     },
16647     
16648     onNodeClick : function(node, e){
16649         this.select(node, e, e.ctrlKey);
16650     },
16651     
16652     /**
16653      * Select a node.
16654      * @param {TreeNode} node The node to select
16655      * @param {EventObject} e (optional) An event associated with the selection
16656      * @param {Boolean} keepExisting True to retain existing selections
16657      * @return {TreeNode} The selected node
16658      */
16659     select : function(node, e, keepExisting){
16660         if(keepExisting !== true){
16661             this.clearSelections(true);
16662         }
16663         if(this.isSelected(node)){
16664             this.lastSelNode = node;
16665             return node;
16666         }
16667         this.selNodes.push(node);
16668         this.selMap[node.id] = node;
16669         this.lastSelNode = node;
16670         node.ui.onSelectedChange(true);
16671         this.fireEvent("selectionchange", this, this.selNodes);
16672         return node;
16673     },
16674     
16675     /**
16676      * Deselect a node.
16677      * @param {TreeNode} node The node to unselect
16678      */
16679     unselect : function(node){
16680         if(this.selMap[node.id]){
16681             node.ui.onSelectedChange(false);
16682             var sn = this.selNodes;
16683             var index = -1;
16684             if(sn.indexOf){
16685                 index = sn.indexOf(node);
16686             }else{
16687                 for(var i = 0, len = sn.length; i < len; i++){
16688                     if(sn[i] == node){
16689                         index = i;
16690                         break;
16691                     }
16692                 }
16693             }
16694             if(index != -1){
16695                 this.selNodes.splice(index, 1);
16696             }
16697             delete this.selMap[node.id];
16698             this.fireEvent("selectionchange", this, this.selNodes);
16699         }
16700     },
16701     
16702     /**
16703      * Clear all selections
16704      */
16705     clearSelections : function(suppressEvent){
16706         var sn = this.selNodes;
16707         if(sn.length > 0){
16708             for(var i = 0, len = sn.length; i < len; i++){
16709                 sn[i].ui.onSelectedChange(false);
16710             }
16711             this.selNodes = [];
16712             this.selMap = {};
16713             if(suppressEvent !== true){
16714                 this.fireEvent("selectionchange", this, this.selNodes);
16715             }
16716         }
16717     },
16718     
16719     /**
16720      * Returns true if the node is selected
16721      * @param {TreeNode} node The node to check
16722      * @return {Boolean}
16723      */
16724     isSelected : function(node){
16725         return this.selMap[node.id] ? true : false;  
16726     },
16727     
16728     /**
16729      * Returns an array of the selected nodes
16730      * @return {Array}
16731      */
16732     getSelectedNodes : function(){
16733         return this.selNodes;    
16734     },
16735
16736     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16737
16738     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16739
16740     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16741 });/*
16742  * Based on:
16743  * Ext JS Library 1.1.1
16744  * Copyright(c) 2006-2007, Ext JS, LLC.
16745  *
16746  * Originally Released Under LGPL - original licence link has changed is not relivant.
16747  *
16748  * Fork - LGPL
16749  * <script type="text/javascript">
16750  */
16751  
16752 /**
16753  * @class Roo.tree.TreeNode
16754  * @extends Roo.data.Node
16755  * @cfg {String} text The text for this node
16756  * @cfg {Boolean} expanded true to start the node expanded
16757  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16758  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16759  * @cfg {Boolean} disabled true to start the node disabled
16760  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16761  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16762  * @cfg {String} cls A css class to be added to the node
16763  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16764  * @cfg {String} href URL of the link used for the node (defaults to #)
16765  * @cfg {String} hrefTarget target frame for the link
16766  * @cfg {String} qtip An Ext QuickTip for the node
16767  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16768  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16769  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16770  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16771  * (defaults to undefined with no checkbox rendered)
16772  * @constructor
16773  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16774  */
16775 Roo.tree.TreeNode = function(attributes){
16776     attributes = attributes || {};
16777     if(typeof attributes == "string"){
16778         attributes = {text: attributes};
16779     }
16780     this.childrenRendered = false;
16781     this.rendered = false;
16782     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16783     this.expanded = attributes.expanded === true;
16784     this.isTarget = attributes.isTarget !== false;
16785     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16786     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16787
16788     /**
16789      * Read-only. The text for this node. To change it use setText().
16790      * @type String
16791      */
16792     this.text = attributes.text;
16793     /**
16794      * True if this node is disabled.
16795      * @type Boolean
16796      */
16797     this.disabled = attributes.disabled === true;
16798
16799     this.addEvents({
16800         /**
16801         * @event textchange
16802         * Fires when the text for this node is changed
16803         * @param {Node} this This node
16804         * @param {String} text The new text
16805         * @param {String} oldText The old text
16806         */
16807         "textchange" : true,
16808         /**
16809         * @event beforeexpand
16810         * Fires before this node is expanded, return false to cancel.
16811         * @param {Node} this This node
16812         * @param {Boolean} deep
16813         * @param {Boolean} anim
16814         */
16815         "beforeexpand" : true,
16816         /**
16817         * @event beforecollapse
16818         * Fires before this node is collapsed, return false to cancel.
16819         * @param {Node} this This node
16820         * @param {Boolean} deep
16821         * @param {Boolean} anim
16822         */
16823         "beforecollapse" : true,
16824         /**
16825         * @event expand
16826         * Fires when this node is expanded
16827         * @param {Node} this This node
16828         */
16829         "expand" : true,
16830         /**
16831         * @event disabledchange
16832         * Fires when the disabled status of this node changes
16833         * @param {Node} this This node
16834         * @param {Boolean} disabled
16835         */
16836         "disabledchange" : true,
16837         /**
16838         * @event collapse
16839         * Fires when this node is collapsed
16840         * @param {Node} this This node
16841         */
16842         "collapse" : true,
16843         /**
16844         * @event beforeclick
16845         * Fires before click processing. Return false to cancel the default action.
16846         * @param {Node} this This node
16847         * @param {Roo.EventObject} e The event object
16848         */
16849         "beforeclick":true,
16850         /**
16851         * @event checkchange
16852         * Fires when a node with a checkbox's checked property changes
16853         * @param {Node} this This node
16854         * @param {Boolean} checked
16855         */
16856         "checkchange":true,
16857         /**
16858         * @event click
16859         * Fires when this node is clicked
16860         * @param {Node} this This node
16861         * @param {Roo.EventObject} e The event object
16862         */
16863         "click":true,
16864         /**
16865         * @event dblclick
16866         * Fires when this node is double clicked
16867         * @param {Node} this This node
16868         * @param {Roo.EventObject} e The event object
16869         */
16870         "dblclick":true,
16871         /**
16872         * @event contextmenu
16873         * Fires when this node is right clicked
16874         * @param {Node} this This node
16875         * @param {Roo.EventObject} e The event object
16876         */
16877         "contextmenu":true,
16878         /**
16879         * @event beforechildrenrendered
16880         * Fires right before the child nodes for this node are rendered
16881         * @param {Node} this This node
16882         */
16883         "beforechildrenrendered":true
16884     });
16885
16886     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16887
16888     /**
16889      * Read-only. The UI for this node
16890      * @type TreeNodeUI
16891      */
16892     this.ui = new uiClass(this);
16893     
16894     // finally support items[]
16895     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16896         return;
16897     }
16898     
16899     
16900     Roo.each(this.attributes.items, function(c) {
16901         this.appendChild(Roo.factory(c,Roo.Tree));
16902     }, this);
16903     delete this.attributes.items;
16904     
16905     
16906     
16907 };
16908 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16909     preventHScroll: true,
16910     /**
16911      * Returns true if this node is expanded
16912      * @return {Boolean}
16913      */
16914     isExpanded : function(){
16915         return this.expanded;
16916     },
16917
16918     /**
16919      * Returns the UI object for this node
16920      * @return {TreeNodeUI}
16921      */
16922     getUI : function(){
16923         return this.ui;
16924     },
16925
16926     // private override
16927     setFirstChild : function(node){
16928         var of = this.firstChild;
16929         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16930         if(this.childrenRendered && of && node != of){
16931             of.renderIndent(true, true);
16932         }
16933         if(this.rendered){
16934             this.renderIndent(true, true);
16935         }
16936     },
16937
16938     // private override
16939     setLastChild : function(node){
16940         var ol = this.lastChild;
16941         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16942         if(this.childrenRendered && ol && node != ol){
16943             ol.renderIndent(true, true);
16944         }
16945         if(this.rendered){
16946             this.renderIndent(true, true);
16947         }
16948     },
16949
16950     // these methods are overridden to provide lazy rendering support
16951     // private override
16952     appendChild : function()
16953     {
16954         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16955         if(node && this.childrenRendered){
16956             node.render();
16957         }
16958         this.ui.updateExpandIcon();
16959         return node;
16960     },
16961
16962     // private override
16963     removeChild : function(node){
16964         this.ownerTree.getSelectionModel().unselect(node);
16965         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16966         // if it's been rendered remove dom node
16967         if(this.childrenRendered){
16968             node.ui.remove();
16969         }
16970         if(this.childNodes.length < 1){
16971             this.collapse(false, false);
16972         }else{
16973             this.ui.updateExpandIcon();
16974         }
16975         if(!this.firstChild) {
16976             this.childrenRendered = false;
16977         }
16978         return node;
16979     },
16980
16981     // private override
16982     insertBefore : function(node, refNode){
16983         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16984         if(newNode && refNode && this.childrenRendered){
16985             node.render();
16986         }
16987         this.ui.updateExpandIcon();
16988         return newNode;
16989     },
16990
16991     /**
16992      * Sets the text for this node
16993      * @param {String} text
16994      */
16995     setText : function(text){
16996         var oldText = this.text;
16997         this.text = text;
16998         this.attributes.text = text;
16999         if(this.rendered){ // event without subscribing
17000             this.ui.onTextChange(this, text, oldText);
17001         }
17002         this.fireEvent("textchange", this, text, oldText);
17003     },
17004
17005     /**
17006      * Triggers selection of this node
17007      */
17008     select : function(){
17009         this.getOwnerTree().getSelectionModel().select(this);
17010     },
17011
17012     /**
17013      * Triggers deselection of this node
17014      */
17015     unselect : function(){
17016         this.getOwnerTree().getSelectionModel().unselect(this);
17017     },
17018
17019     /**
17020      * Returns true if this node is selected
17021      * @return {Boolean}
17022      */
17023     isSelected : function(){
17024         return this.getOwnerTree().getSelectionModel().isSelected(this);
17025     },
17026
17027     /**
17028      * Expand this node.
17029      * @param {Boolean} deep (optional) True to expand all children as well
17030      * @param {Boolean} anim (optional) false to cancel the default animation
17031      * @param {Function} callback (optional) A callback to be called when
17032      * expanding this node completes (does not wait for deep expand to complete).
17033      * Called with 1 parameter, this node.
17034      */
17035     expand : function(deep, anim, callback){
17036         if(!this.expanded){
17037             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17038                 return;
17039             }
17040             if(!this.childrenRendered){
17041                 this.renderChildren();
17042             }
17043             this.expanded = true;
17044             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17045                 this.ui.animExpand(function(){
17046                     this.fireEvent("expand", this);
17047                     if(typeof callback == "function"){
17048                         callback(this);
17049                     }
17050                     if(deep === true){
17051                         this.expandChildNodes(true);
17052                     }
17053                 }.createDelegate(this));
17054                 return;
17055             }else{
17056                 this.ui.expand();
17057                 this.fireEvent("expand", this);
17058                 if(typeof callback == "function"){
17059                     callback(this);
17060                 }
17061             }
17062         }else{
17063            if(typeof callback == "function"){
17064                callback(this);
17065            }
17066         }
17067         if(deep === true){
17068             this.expandChildNodes(true);
17069         }
17070     },
17071
17072     isHiddenRoot : function(){
17073         return this.isRoot && !this.getOwnerTree().rootVisible;
17074     },
17075
17076     /**
17077      * Collapse this node.
17078      * @param {Boolean} deep (optional) True to collapse all children as well
17079      * @param {Boolean} anim (optional) false to cancel the default animation
17080      */
17081     collapse : function(deep, anim){
17082         if(this.expanded && !this.isHiddenRoot()){
17083             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17084                 return;
17085             }
17086             this.expanded = false;
17087             if((this.getOwnerTree().animate && anim !== false) || anim){
17088                 this.ui.animCollapse(function(){
17089                     this.fireEvent("collapse", this);
17090                     if(deep === true){
17091                         this.collapseChildNodes(true);
17092                     }
17093                 }.createDelegate(this));
17094                 return;
17095             }else{
17096                 this.ui.collapse();
17097                 this.fireEvent("collapse", this);
17098             }
17099         }
17100         if(deep === true){
17101             var cs = this.childNodes;
17102             for(var i = 0, len = cs.length; i < len; i++) {
17103                 cs[i].collapse(true, false);
17104             }
17105         }
17106     },
17107
17108     // private
17109     delayedExpand : function(delay){
17110         if(!this.expandProcId){
17111             this.expandProcId = this.expand.defer(delay, this);
17112         }
17113     },
17114
17115     // private
17116     cancelExpand : function(){
17117         if(this.expandProcId){
17118             clearTimeout(this.expandProcId);
17119         }
17120         this.expandProcId = false;
17121     },
17122
17123     /**
17124      * Toggles expanded/collapsed state of the node
17125      */
17126     toggle : function(){
17127         if(this.expanded){
17128             this.collapse();
17129         }else{
17130             this.expand();
17131         }
17132     },
17133
17134     /**
17135      * Ensures all parent nodes are expanded
17136      */
17137     ensureVisible : function(callback){
17138         var tree = this.getOwnerTree();
17139         tree.expandPath(this.parentNode.getPath(), false, function(){
17140             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17141             Roo.callback(callback);
17142         }.createDelegate(this));
17143     },
17144
17145     /**
17146      * Expand all child nodes
17147      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17148      */
17149     expandChildNodes : function(deep){
17150         var cs = this.childNodes;
17151         for(var i = 0, len = cs.length; i < len; i++) {
17152                 cs[i].expand(deep);
17153         }
17154     },
17155
17156     /**
17157      * Collapse all child nodes
17158      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17159      */
17160     collapseChildNodes : function(deep){
17161         var cs = this.childNodes;
17162         for(var i = 0, len = cs.length; i < len; i++) {
17163                 cs[i].collapse(deep);
17164         }
17165     },
17166
17167     /**
17168      * Disables this node
17169      */
17170     disable : function(){
17171         this.disabled = true;
17172         this.unselect();
17173         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17174             this.ui.onDisableChange(this, true);
17175         }
17176         this.fireEvent("disabledchange", this, true);
17177     },
17178
17179     /**
17180      * Enables this node
17181      */
17182     enable : function(){
17183         this.disabled = false;
17184         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17185             this.ui.onDisableChange(this, false);
17186         }
17187         this.fireEvent("disabledchange", this, false);
17188     },
17189
17190     // private
17191     renderChildren : function(suppressEvent){
17192         if(suppressEvent !== false){
17193             this.fireEvent("beforechildrenrendered", this);
17194         }
17195         var cs = this.childNodes;
17196         for(var i = 0, len = cs.length; i < len; i++){
17197             cs[i].render(true);
17198         }
17199         this.childrenRendered = true;
17200     },
17201
17202     // private
17203     sort : function(fn, scope){
17204         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17205         if(this.childrenRendered){
17206             var cs = this.childNodes;
17207             for(var i = 0, len = cs.length; i < len; i++){
17208                 cs[i].render(true);
17209             }
17210         }
17211     },
17212
17213     // private
17214     render : function(bulkRender){
17215         this.ui.render(bulkRender);
17216         if(!this.rendered){
17217             this.rendered = true;
17218             if(this.expanded){
17219                 this.expanded = false;
17220                 this.expand(false, false);
17221             }
17222         }
17223     },
17224
17225     // private
17226     renderIndent : function(deep, refresh){
17227         if(refresh){
17228             this.ui.childIndent = null;
17229         }
17230         this.ui.renderIndent();
17231         if(deep === true && this.childrenRendered){
17232             var cs = this.childNodes;
17233             for(var i = 0, len = cs.length; i < len; i++){
17234                 cs[i].renderIndent(true, refresh);
17235             }
17236         }
17237     }
17238 });/*
17239  * Based on:
17240  * Ext JS Library 1.1.1
17241  * Copyright(c) 2006-2007, Ext JS, LLC.
17242  *
17243  * Originally Released Under LGPL - original licence link has changed is not relivant.
17244  *
17245  * Fork - LGPL
17246  * <script type="text/javascript">
17247  */
17248  
17249 /**
17250  * @class Roo.tree.AsyncTreeNode
17251  * @extends Roo.tree.TreeNode
17252  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17253  * @constructor
17254  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17255  */
17256  Roo.tree.AsyncTreeNode = function(config){
17257     this.loaded = false;
17258     this.loading = false;
17259     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17260     /**
17261     * @event beforeload
17262     * Fires before this node is loaded, return false to cancel
17263     * @param {Node} this This node
17264     */
17265     this.addEvents({'beforeload':true, 'load': true});
17266     /**
17267     * @event load
17268     * Fires when this node is loaded
17269     * @param {Node} this This node
17270     */
17271     /**
17272      * The loader used by this node (defaults to using the tree's defined loader)
17273      * @type TreeLoader
17274      * @property loader
17275      */
17276 };
17277 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17278     expand : function(deep, anim, callback){
17279         if(this.loading){ // if an async load is already running, waiting til it's done
17280             var timer;
17281             var f = function(){
17282                 if(!this.loading){ // done loading
17283                     clearInterval(timer);
17284                     this.expand(deep, anim, callback);
17285                 }
17286             }.createDelegate(this);
17287             timer = setInterval(f, 200);
17288             return;
17289         }
17290         if(!this.loaded){
17291             if(this.fireEvent("beforeload", this) === false){
17292                 return;
17293             }
17294             this.loading = true;
17295             this.ui.beforeLoad(this);
17296             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17297             if(loader){
17298                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17299                 return;
17300             }
17301         }
17302         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17303     },
17304     
17305     /**
17306      * Returns true if this node is currently loading
17307      * @return {Boolean}
17308      */
17309     isLoading : function(){
17310         return this.loading;  
17311     },
17312     
17313     loadComplete : function(deep, anim, callback){
17314         this.loading = false;
17315         this.loaded = true;
17316         this.ui.afterLoad(this);
17317         this.fireEvent("load", this);
17318         this.expand(deep, anim, callback);
17319     },
17320     
17321     /**
17322      * Returns true if this node has been loaded
17323      * @return {Boolean}
17324      */
17325     isLoaded : function(){
17326         return this.loaded;
17327     },
17328     
17329     hasChildNodes : function(){
17330         if(!this.isLeaf() && !this.loaded){
17331             return true;
17332         }else{
17333             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17334         }
17335     },
17336
17337     /**
17338      * Trigger a reload for this node
17339      * @param {Function} callback
17340      */
17341     reload : function(callback){
17342         this.collapse(false, false);
17343         while(this.firstChild){
17344             this.removeChild(this.firstChild);
17345         }
17346         this.childrenRendered = false;
17347         this.loaded = false;
17348         if(this.isHiddenRoot()){
17349             this.expanded = false;
17350         }
17351         this.expand(false, false, callback);
17352     }
17353 });/*
17354  * Based on:
17355  * Ext JS Library 1.1.1
17356  * Copyright(c) 2006-2007, Ext JS, LLC.
17357  *
17358  * Originally Released Under LGPL - original licence link has changed is not relivant.
17359  *
17360  * Fork - LGPL
17361  * <script type="text/javascript">
17362  */
17363  
17364 /**
17365  * @class Roo.tree.TreeNodeUI
17366  * @constructor
17367  * @param {Object} node The node to render
17368  * The TreeNode UI implementation is separate from the
17369  * tree implementation. Unless you are customizing the tree UI,
17370  * you should never have to use this directly.
17371  */
17372 Roo.tree.TreeNodeUI = function(node){
17373     this.node = node;
17374     this.rendered = false;
17375     this.animating = false;
17376     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17377 };
17378
17379 Roo.tree.TreeNodeUI.prototype = {
17380     removeChild : function(node){
17381         if(this.rendered){
17382             this.ctNode.removeChild(node.ui.getEl());
17383         }
17384     },
17385
17386     beforeLoad : function(){
17387          this.addClass("x-tree-node-loading");
17388     },
17389
17390     afterLoad : function(){
17391          this.removeClass("x-tree-node-loading");
17392     },
17393
17394     onTextChange : function(node, text, oldText){
17395         if(this.rendered){
17396             this.textNode.innerHTML = text;
17397         }
17398     },
17399
17400     onDisableChange : function(node, state){
17401         this.disabled = state;
17402         if(state){
17403             this.addClass("x-tree-node-disabled");
17404         }else{
17405             this.removeClass("x-tree-node-disabled");
17406         }
17407     },
17408
17409     onSelectedChange : function(state){
17410         if(state){
17411             this.focus();
17412             this.addClass("x-tree-selected");
17413         }else{
17414             //this.blur();
17415             this.removeClass("x-tree-selected");
17416         }
17417     },
17418
17419     onMove : function(tree, node, oldParent, newParent, index, refNode){
17420         this.childIndent = null;
17421         if(this.rendered){
17422             var targetNode = newParent.ui.getContainer();
17423             if(!targetNode){//target not rendered
17424                 this.holder = document.createElement("div");
17425                 this.holder.appendChild(this.wrap);
17426                 return;
17427             }
17428             var insertBefore = refNode ? refNode.ui.getEl() : null;
17429             if(insertBefore){
17430                 targetNode.insertBefore(this.wrap, insertBefore);
17431             }else{
17432                 targetNode.appendChild(this.wrap);
17433             }
17434             this.node.renderIndent(true);
17435         }
17436     },
17437
17438     addClass : function(cls){
17439         if(this.elNode){
17440             Roo.fly(this.elNode).addClass(cls);
17441         }
17442     },
17443
17444     removeClass : function(cls){
17445         if(this.elNode){
17446             Roo.fly(this.elNode).removeClass(cls);
17447         }
17448     },
17449
17450     remove : function(){
17451         if(this.rendered){
17452             this.holder = document.createElement("div");
17453             this.holder.appendChild(this.wrap);
17454         }
17455     },
17456
17457     fireEvent : function(){
17458         return this.node.fireEvent.apply(this.node, arguments);
17459     },
17460
17461     initEvents : function(){
17462         this.node.on("move", this.onMove, this);
17463         var E = Roo.EventManager;
17464         var a = this.anchor;
17465
17466         var el = Roo.fly(a, '_treeui');
17467
17468         if(Roo.isOpera){ // opera render bug ignores the CSS
17469             el.setStyle("text-decoration", "none");
17470         }
17471
17472         el.on("click", this.onClick, this);
17473         el.on("dblclick", this.onDblClick, this);
17474
17475         if(this.checkbox){
17476             Roo.EventManager.on(this.checkbox,
17477                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17478         }
17479
17480         el.on("contextmenu", this.onContextMenu, this);
17481
17482         var icon = Roo.fly(this.iconNode);
17483         icon.on("click", this.onClick, this);
17484         icon.on("dblclick", this.onDblClick, this);
17485         icon.on("contextmenu", this.onContextMenu, this);
17486         E.on(this.ecNode, "click", this.ecClick, this, true);
17487
17488         if(this.node.disabled){
17489             this.addClass("x-tree-node-disabled");
17490         }
17491         if(this.node.hidden){
17492             this.addClass("x-tree-node-disabled");
17493         }
17494         var ot = this.node.getOwnerTree();
17495         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17496         if(dd && (!this.node.isRoot || ot.rootVisible)){
17497             Roo.dd.Registry.register(this.elNode, {
17498                 node: this.node,
17499                 handles: this.getDDHandles(),
17500                 isHandle: false
17501             });
17502         }
17503     },
17504
17505     getDDHandles : function(){
17506         return [this.iconNode, this.textNode];
17507     },
17508
17509     hide : function(){
17510         if(this.rendered){
17511             this.wrap.style.display = "none";
17512         }
17513     },
17514
17515     show : function(){
17516         if(this.rendered){
17517             this.wrap.style.display = "";
17518         }
17519     },
17520
17521     onContextMenu : function(e){
17522         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17523             e.preventDefault();
17524             this.focus();
17525             this.fireEvent("contextmenu", this.node, e);
17526         }
17527     },
17528
17529     onClick : function(e){
17530         if(this.dropping){
17531             e.stopEvent();
17532             return;
17533         }
17534         if(this.fireEvent("beforeclick", this.node, e) !== false){
17535             if(!this.disabled && this.node.attributes.href){
17536                 this.fireEvent("click", this.node, e);
17537                 return;
17538             }
17539             e.preventDefault();
17540             if(this.disabled){
17541                 return;
17542             }
17543
17544             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17545                 this.node.toggle();
17546             }
17547
17548             this.fireEvent("click", this.node, e);
17549         }else{
17550             e.stopEvent();
17551         }
17552     },
17553
17554     onDblClick : function(e){
17555         e.preventDefault();
17556         if(this.disabled){
17557             return;
17558         }
17559         if(this.checkbox){
17560             this.toggleCheck();
17561         }
17562         if(!this.animating && this.node.hasChildNodes()){
17563             this.node.toggle();
17564         }
17565         this.fireEvent("dblclick", this.node, e);
17566     },
17567
17568     onCheckChange : function(){
17569         var checked = this.checkbox.checked;
17570         this.node.attributes.checked = checked;
17571         this.fireEvent('checkchange', this.node, checked);
17572     },
17573
17574     ecClick : function(e){
17575         if(!this.animating && this.node.hasChildNodes()){
17576             this.node.toggle();
17577         }
17578     },
17579
17580     startDrop : function(){
17581         this.dropping = true;
17582     },
17583
17584     // delayed drop so the click event doesn't get fired on a drop
17585     endDrop : function(){
17586        setTimeout(function(){
17587            this.dropping = false;
17588        }.createDelegate(this), 50);
17589     },
17590
17591     expand : function(){
17592         this.updateExpandIcon();
17593         this.ctNode.style.display = "";
17594     },
17595
17596     focus : function(){
17597         if(!this.node.preventHScroll){
17598             try{this.anchor.focus();
17599             }catch(e){}
17600         }else if(!Roo.isIE){
17601             try{
17602                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17603                 var l = noscroll.scrollLeft;
17604                 this.anchor.focus();
17605                 noscroll.scrollLeft = l;
17606             }catch(e){}
17607         }
17608     },
17609
17610     toggleCheck : function(value){
17611         var cb = this.checkbox;
17612         if(cb){
17613             cb.checked = (value === undefined ? !cb.checked : value);
17614         }
17615     },
17616
17617     blur : function(){
17618         try{
17619             this.anchor.blur();
17620         }catch(e){}
17621     },
17622
17623     animExpand : function(callback){
17624         var ct = Roo.get(this.ctNode);
17625         ct.stopFx();
17626         if(!this.node.hasChildNodes()){
17627             this.updateExpandIcon();
17628             this.ctNode.style.display = "";
17629             Roo.callback(callback);
17630             return;
17631         }
17632         this.animating = true;
17633         this.updateExpandIcon();
17634
17635         ct.slideIn('t', {
17636            callback : function(){
17637                this.animating = false;
17638                Roo.callback(callback);
17639             },
17640             scope: this,
17641             duration: this.node.ownerTree.duration || .25
17642         });
17643     },
17644
17645     highlight : function(){
17646         var tree = this.node.getOwnerTree();
17647         Roo.fly(this.wrap).highlight(
17648             tree.hlColor || "C3DAF9",
17649             {endColor: tree.hlBaseColor}
17650         );
17651     },
17652
17653     collapse : function(){
17654         this.updateExpandIcon();
17655         this.ctNode.style.display = "none";
17656     },
17657
17658     animCollapse : function(callback){
17659         var ct = Roo.get(this.ctNode);
17660         ct.enableDisplayMode('block');
17661         ct.stopFx();
17662
17663         this.animating = true;
17664         this.updateExpandIcon();
17665
17666         ct.slideOut('t', {
17667             callback : function(){
17668                this.animating = false;
17669                Roo.callback(callback);
17670             },
17671             scope: this,
17672             duration: this.node.ownerTree.duration || .25
17673         });
17674     },
17675
17676     getContainer : function(){
17677         return this.ctNode;
17678     },
17679
17680     getEl : function(){
17681         return this.wrap;
17682     },
17683
17684     appendDDGhost : function(ghostNode){
17685         ghostNode.appendChild(this.elNode.cloneNode(true));
17686     },
17687
17688     getDDRepairXY : function(){
17689         return Roo.lib.Dom.getXY(this.iconNode);
17690     },
17691
17692     onRender : function(){
17693         this.render();
17694     },
17695
17696     render : function(bulkRender){
17697         var n = this.node, a = n.attributes;
17698         var targetNode = n.parentNode ?
17699               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17700
17701         if(!this.rendered){
17702             this.rendered = true;
17703
17704             this.renderElements(n, a, targetNode, bulkRender);
17705
17706             if(a.qtip){
17707                if(this.textNode.setAttributeNS){
17708                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17709                    if(a.qtipTitle){
17710                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17711                    }
17712                }else{
17713                    this.textNode.setAttribute("ext:qtip", a.qtip);
17714                    if(a.qtipTitle){
17715                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17716                    }
17717                }
17718             }else if(a.qtipCfg){
17719                 a.qtipCfg.target = Roo.id(this.textNode);
17720                 Roo.QuickTips.register(a.qtipCfg);
17721             }
17722             this.initEvents();
17723             if(!this.node.expanded){
17724                 this.updateExpandIcon();
17725             }
17726         }else{
17727             if(bulkRender === true) {
17728                 targetNode.appendChild(this.wrap);
17729             }
17730         }
17731     },
17732
17733     renderElements : function(n, a, targetNode, bulkRender)
17734     {
17735         // add some indent caching, this helps performance when rendering a large tree
17736         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17737         var t = n.getOwnerTree();
17738         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17739         if (typeof(n.attributes.html) != 'undefined') {
17740             txt = n.attributes.html;
17741         }
17742         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17743         var cb = typeof a.checked == 'boolean';
17744         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17745         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17746             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17747             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17748             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17749             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17750             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17751              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17752                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17753             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17754             "</li>"];
17755
17756         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17757             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17758                                 n.nextSibling.ui.getEl(), buf.join(""));
17759         }else{
17760             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17761         }
17762
17763         this.elNode = this.wrap.childNodes[0];
17764         this.ctNode = this.wrap.childNodes[1];
17765         var cs = this.elNode.childNodes;
17766         this.indentNode = cs[0];
17767         this.ecNode = cs[1];
17768         this.iconNode = cs[2];
17769         var index = 3;
17770         if(cb){
17771             this.checkbox = cs[3];
17772             index++;
17773         }
17774         this.anchor = cs[index];
17775         this.textNode = cs[index].firstChild;
17776     },
17777
17778     getAnchor : function(){
17779         return this.anchor;
17780     },
17781
17782     getTextEl : function(){
17783         return this.textNode;
17784     },
17785
17786     getIconEl : function(){
17787         return this.iconNode;
17788     },
17789
17790     isChecked : function(){
17791         return this.checkbox ? this.checkbox.checked : false;
17792     },
17793
17794     updateExpandIcon : function(){
17795         if(this.rendered){
17796             var n = this.node, c1, c2;
17797             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17798             var hasChild = n.hasChildNodes();
17799             if(hasChild){
17800                 if(n.expanded){
17801                     cls += "-minus";
17802                     c1 = "x-tree-node-collapsed";
17803                     c2 = "x-tree-node-expanded";
17804                 }else{
17805                     cls += "-plus";
17806                     c1 = "x-tree-node-expanded";
17807                     c2 = "x-tree-node-collapsed";
17808                 }
17809                 if(this.wasLeaf){
17810                     this.removeClass("x-tree-node-leaf");
17811                     this.wasLeaf = false;
17812                 }
17813                 if(this.c1 != c1 || this.c2 != c2){
17814                     Roo.fly(this.elNode).replaceClass(c1, c2);
17815                     this.c1 = c1; this.c2 = c2;
17816                 }
17817             }else{
17818                 // this changes non-leafs into leafs if they have no children.
17819                 // it's not very rational behaviour..
17820                 
17821                 if(!this.wasLeaf && this.node.leaf){
17822                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17823                     delete this.c1;
17824                     delete this.c2;
17825                     this.wasLeaf = true;
17826                 }
17827             }
17828             var ecc = "x-tree-ec-icon "+cls;
17829             if(this.ecc != ecc){
17830                 this.ecNode.className = ecc;
17831                 this.ecc = ecc;
17832             }
17833         }
17834     },
17835
17836     getChildIndent : function(){
17837         if(!this.childIndent){
17838             var buf = [];
17839             var p = this.node;
17840             while(p){
17841                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17842                     if(!p.isLast()) {
17843                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17844                     } else {
17845                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17846                     }
17847                 }
17848                 p = p.parentNode;
17849             }
17850             this.childIndent = buf.join("");
17851         }
17852         return this.childIndent;
17853     },
17854
17855     renderIndent : function(){
17856         if(this.rendered){
17857             var indent = "";
17858             var p = this.node.parentNode;
17859             if(p){
17860                 indent = p.ui.getChildIndent();
17861             }
17862             if(this.indentMarkup != indent){ // don't rerender if not required
17863                 this.indentNode.innerHTML = indent;
17864                 this.indentMarkup = indent;
17865             }
17866             this.updateExpandIcon();
17867         }
17868     }
17869 };
17870
17871 Roo.tree.RootTreeNodeUI = function(){
17872     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17873 };
17874 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17875     render : function(){
17876         if(!this.rendered){
17877             var targetNode = this.node.ownerTree.innerCt.dom;
17878             this.node.expanded = true;
17879             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17880             this.wrap = this.ctNode = targetNode.firstChild;
17881         }
17882     },
17883     collapse : function(){
17884     },
17885     expand : function(){
17886     }
17887 });/*
17888  * Based on:
17889  * Ext JS Library 1.1.1
17890  * Copyright(c) 2006-2007, Ext JS, LLC.
17891  *
17892  * Originally Released Under LGPL - original licence link has changed is not relivant.
17893  *
17894  * Fork - LGPL
17895  * <script type="text/javascript">
17896  */
17897 /**
17898  * @class Roo.tree.TreeLoader
17899  * @extends Roo.util.Observable
17900  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17901  * nodes from a specified URL. The response must be a javascript Array definition
17902  * who's elements are node definition objects. eg:
17903  * <pre><code>
17904 {  success : true,
17905    data :      [
17906    
17907     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17908     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17909     ]
17910 }
17911
17912
17913 </code></pre>
17914  * <br><br>
17915  * The old style respose with just an array is still supported, but not recommended.
17916  * <br><br>
17917  *
17918  * A server request is sent, and child nodes are loaded only when a node is expanded.
17919  * The loading node's id is passed to the server under the parameter name "node" to
17920  * enable the server to produce the correct child nodes.
17921  * <br><br>
17922  * To pass extra parameters, an event handler may be attached to the "beforeload"
17923  * event, and the parameters specified in the TreeLoader's baseParams property:
17924  * <pre><code>
17925     myTreeLoader.on("beforeload", function(treeLoader, node) {
17926         this.baseParams.category = node.attributes.category;
17927     }, this);
17928 </code></pre><
17929  * This would pass an HTTP parameter called "category" to the server containing
17930  * the value of the Node's "category" attribute.
17931  * @constructor
17932  * Creates a new Treeloader.
17933  * @param {Object} config A config object containing config properties.
17934  */
17935 Roo.tree.TreeLoader = function(config){
17936     this.baseParams = {};
17937     this.requestMethod = "POST";
17938     Roo.apply(this, config);
17939
17940     this.addEvents({
17941     
17942         /**
17943          * @event beforeload
17944          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17945          * @param {Object} This TreeLoader object.
17946          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17947          * @param {Object} callback The callback function specified in the {@link #load} call.
17948          */
17949         beforeload : true,
17950         /**
17951          * @event load
17952          * Fires when the node has been successfuly loaded.
17953          * @param {Object} This TreeLoader object.
17954          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17955          * @param {Object} response The response object containing the data from the server.
17956          */
17957         load : true,
17958         /**
17959          * @event loadexception
17960          * Fires if the network request failed.
17961          * @param {Object} This TreeLoader object.
17962          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17963          * @param {Object} response The response object containing the data from the server.
17964          */
17965         loadexception : true,
17966         /**
17967          * @event create
17968          * Fires before a node is created, enabling you to return custom Node types 
17969          * @param {Object} This TreeLoader object.
17970          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17971          */
17972         create : true
17973     });
17974
17975     Roo.tree.TreeLoader.superclass.constructor.call(this);
17976 };
17977
17978 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17979     /**
17980     * @cfg {String} dataUrl The URL from which to request a Json string which
17981     * specifies an array of node definition object representing the child nodes
17982     * to be loaded.
17983     */
17984     /**
17985     * @cfg {String} requestMethod either GET or POST
17986     * defaults to POST (due to BC)
17987     * to be loaded.
17988     */
17989     /**
17990     * @cfg {Object} baseParams (optional) An object containing properties which
17991     * specify HTTP parameters to be passed to each request for child nodes.
17992     */
17993     /**
17994     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17995     * created by this loader. If the attributes sent by the server have an attribute in this object,
17996     * they take priority.
17997     */
17998     /**
17999     * @cfg {Object} uiProviders (optional) An object containing properties which
18000     * 
18001     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18002     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18003     * <i>uiProvider</i> attribute of a returned child node is a string rather
18004     * than a reference to a TreeNodeUI implementation, this that string value
18005     * is used as a property name in the uiProviders object. You can define the provider named
18006     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18007     */
18008     uiProviders : {},
18009
18010     /**
18011     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18012     * child nodes before loading.
18013     */
18014     clearOnLoad : true,
18015
18016     /**
18017     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18018     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18019     * Grid query { data : [ .....] }
18020     */
18021     
18022     root : false,
18023      /**
18024     * @cfg {String} queryParam (optional) 
18025     * Name of the query as it will be passed on the querystring (defaults to 'node')
18026     * eg. the request will be ?node=[id]
18027     */
18028     
18029     
18030     queryParam: false,
18031     
18032     /**
18033      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18034      * This is called automatically when a node is expanded, but may be used to reload
18035      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18036      * @param {Roo.tree.TreeNode} node
18037      * @param {Function} callback
18038      */
18039     load : function(node, callback){
18040         if(this.clearOnLoad){
18041             while(node.firstChild){
18042                 node.removeChild(node.firstChild);
18043             }
18044         }
18045         if(node.attributes.children){ // preloaded json children
18046             var cs = node.attributes.children;
18047             for(var i = 0, len = cs.length; i < len; i++){
18048                 node.appendChild(this.createNode(cs[i]));
18049             }
18050             if(typeof callback == "function"){
18051                 callback();
18052             }
18053         }else if(this.dataUrl){
18054             this.requestData(node, callback);
18055         }
18056     },
18057
18058     getParams: function(node){
18059         var buf = [], bp = this.baseParams;
18060         for(var key in bp){
18061             if(typeof bp[key] != "function"){
18062                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18063             }
18064         }
18065         var n = this.queryParam === false ? 'node' : this.queryParam;
18066         buf.push(n + "=", encodeURIComponent(node.id));
18067         return buf.join("");
18068     },
18069
18070     requestData : function(node, callback){
18071         if(this.fireEvent("beforeload", this, node, callback) !== false){
18072             this.transId = Roo.Ajax.request({
18073                 method:this.requestMethod,
18074                 url: this.dataUrl||this.url,
18075                 success: this.handleResponse,
18076                 failure: this.handleFailure,
18077                 scope: this,
18078                 argument: {callback: callback, node: node},
18079                 params: this.getParams(node)
18080             });
18081         }else{
18082             // if the load is cancelled, make sure we notify
18083             // the node that we are done
18084             if(typeof callback == "function"){
18085                 callback();
18086             }
18087         }
18088     },
18089
18090     isLoading : function(){
18091         return this.transId ? true : false;
18092     },
18093
18094     abort : function(){
18095         if(this.isLoading()){
18096             Roo.Ajax.abort(this.transId);
18097         }
18098     },
18099
18100     // private
18101     createNode : function(attr)
18102     {
18103         // apply baseAttrs, nice idea Corey!
18104         if(this.baseAttrs){
18105             Roo.applyIf(attr, this.baseAttrs);
18106         }
18107         if(this.applyLoader !== false){
18108             attr.loader = this;
18109         }
18110         // uiProvider = depreciated..
18111         
18112         if(typeof(attr.uiProvider) == 'string'){
18113            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18114                 /**  eval:var:attr */ eval(attr.uiProvider);
18115         }
18116         if(typeof(this.uiProviders['default']) != 'undefined') {
18117             attr.uiProvider = this.uiProviders['default'];
18118         }
18119         
18120         this.fireEvent('create', this, attr);
18121         
18122         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18123         return(attr.leaf ?
18124                         new Roo.tree.TreeNode(attr) :
18125                         new Roo.tree.AsyncTreeNode(attr));
18126     },
18127
18128     processResponse : function(response, node, callback)
18129     {
18130         var json = response.responseText;
18131         try {
18132             
18133             var o = Roo.decode(json);
18134             
18135             if (this.root === false && typeof(o.success) != undefined) {
18136                 this.root = 'data'; // the default behaviour for list like data..
18137                 }
18138                 
18139             if (this.root !== false &&  !o.success) {
18140                 // it's a failure condition.
18141                 var a = response.argument;
18142                 this.fireEvent("loadexception", this, a.node, response);
18143                 Roo.log("Load failed - should have a handler really");
18144                 return;
18145             }
18146             
18147             
18148             
18149             if (this.root !== false) {
18150                  o = o[this.root];
18151             }
18152             
18153             for(var i = 0, len = o.length; i < len; i++){
18154                 var n = this.createNode(o[i]);
18155                 if(n){
18156                     node.appendChild(n);
18157                 }
18158             }
18159             if(typeof callback == "function"){
18160                 callback(this, node);
18161             }
18162         }catch(e){
18163             this.handleFailure(response);
18164         }
18165     },
18166
18167     handleResponse : function(response){
18168         this.transId = false;
18169         var a = response.argument;
18170         this.processResponse(response, a.node, a.callback);
18171         this.fireEvent("load", this, a.node, response);
18172     },
18173
18174     handleFailure : function(response)
18175     {
18176         // should handle failure better..
18177         this.transId = false;
18178         var a = response.argument;
18179         this.fireEvent("loadexception", this, a.node, response);
18180         if(typeof a.callback == "function"){
18181             a.callback(this, a.node);
18182         }
18183     }
18184 });/*
18185  * Based on:
18186  * Ext JS Library 1.1.1
18187  * Copyright(c) 2006-2007, Ext JS, LLC.
18188  *
18189  * Originally Released Under LGPL - original licence link has changed is not relivant.
18190  *
18191  * Fork - LGPL
18192  * <script type="text/javascript">
18193  */
18194
18195 /**
18196 * @class Roo.tree.TreeFilter
18197 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18198 * @param {TreePanel} tree
18199 * @param {Object} config (optional)
18200  */
18201 Roo.tree.TreeFilter = function(tree, config){
18202     this.tree = tree;
18203     this.filtered = {};
18204     Roo.apply(this, config);
18205 };
18206
18207 Roo.tree.TreeFilter.prototype = {
18208     clearBlank:false,
18209     reverse:false,
18210     autoClear:false,
18211     remove:false,
18212
18213      /**
18214      * Filter the data by a specific attribute.
18215      * @param {String/RegExp} value Either string that the attribute value
18216      * should start with or a RegExp to test against the attribute
18217      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18218      * @param {TreeNode} startNode (optional) The node to start the filter at.
18219      */
18220     filter : function(value, attr, startNode){
18221         attr = attr || "text";
18222         var f;
18223         if(typeof value == "string"){
18224             var vlen = value.length;
18225             // auto clear empty filter
18226             if(vlen == 0 && this.clearBlank){
18227                 this.clear();
18228                 return;
18229             }
18230             value = value.toLowerCase();
18231             f = function(n){
18232                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18233             };
18234         }else if(value.exec){ // regex?
18235             f = function(n){
18236                 return value.test(n.attributes[attr]);
18237             };
18238         }else{
18239             throw 'Illegal filter type, must be string or regex';
18240         }
18241         this.filterBy(f, null, startNode);
18242         },
18243
18244     /**
18245      * Filter by a function. The passed function will be called with each
18246      * node in the tree (or from the startNode). If the function returns true, the node is kept
18247      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18248      * @param {Function} fn The filter function
18249      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18250      */
18251     filterBy : function(fn, scope, startNode){
18252         startNode = startNode || this.tree.root;
18253         if(this.autoClear){
18254             this.clear();
18255         }
18256         var af = this.filtered, rv = this.reverse;
18257         var f = function(n){
18258             if(n == startNode){
18259                 return true;
18260             }
18261             if(af[n.id]){
18262                 return false;
18263             }
18264             var m = fn.call(scope || n, n);
18265             if(!m || rv){
18266                 af[n.id] = n;
18267                 n.ui.hide();
18268                 return false;
18269             }
18270             return true;
18271         };
18272         startNode.cascade(f);
18273         if(this.remove){
18274            for(var id in af){
18275                if(typeof id != "function"){
18276                    var n = af[id];
18277                    if(n && n.parentNode){
18278                        n.parentNode.removeChild(n);
18279                    }
18280                }
18281            }
18282         }
18283     },
18284
18285     /**
18286      * Clears the current filter. Note: with the "remove" option
18287      * set a filter cannot be cleared.
18288      */
18289     clear : function(){
18290         var t = this.tree;
18291         var af = this.filtered;
18292         for(var id in af){
18293             if(typeof id != "function"){
18294                 var n = af[id];
18295                 if(n){
18296                     n.ui.show();
18297                 }
18298             }
18299         }
18300         this.filtered = {};
18301     }
18302 };
18303 /*
18304  * Based on:
18305  * Ext JS Library 1.1.1
18306  * Copyright(c) 2006-2007, Ext JS, LLC.
18307  *
18308  * Originally Released Under LGPL - original licence link has changed is not relivant.
18309  *
18310  * Fork - LGPL
18311  * <script type="text/javascript">
18312  */
18313  
18314
18315 /**
18316  * @class Roo.tree.TreeSorter
18317  * Provides sorting of nodes in a TreePanel
18318  * 
18319  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18320  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18321  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18322  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18323  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18324  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18325  * @constructor
18326  * @param {TreePanel} tree
18327  * @param {Object} config
18328  */
18329 Roo.tree.TreeSorter = function(tree, config){
18330     Roo.apply(this, config);
18331     tree.on("beforechildrenrendered", this.doSort, this);
18332     tree.on("append", this.updateSort, this);
18333     tree.on("insert", this.updateSort, this);
18334     
18335     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18336     var p = this.property || "text";
18337     var sortType = this.sortType;
18338     var fs = this.folderSort;
18339     var cs = this.caseSensitive === true;
18340     var leafAttr = this.leafAttr || 'leaf';
18341
18342     this.sortFn = function(n1, n2){
18343         if(fs){
18344             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18345                 return 1;
18346             }
18347             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18348                 return -1;
18349             }
18350         }
18351         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18352         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18353         if(v1 < v2){
18354                         return dsc ? +1 : -1;
18355                 }else if(v1 > v2){
18356                         return dsc ? -1 : +1;
18357         }else{
18358                 return 0;
18359         }
18360     };
18361 };
18362
18363 Roo.tree.TreeSorter.prototype = {
18364     doSort : function(node){
18365         node.sort(this.sortFn);
18366     },
18367     
18368     compareNodes : function(n1, n2){
18369         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18370     },
18371     
18372     updateSort : function(tree, node){
18373         if(node.childrenRendered){
18374             this.doSort.defer(1, this, [node]);
18375         }
18376     }
18377 };/*
18378  * Based on:
18379  * Ext JS Library 1.1.1
18380  * Copyright(c) 2006-2007, Ext JS, LLC.
18381  *
18382  * Originally Released Under LGPL - original licence link has changed is not relivant.
18383  *
18384  * Fork - LGPL
18385  * <script type="text/javascript">
18386  */
18387
18388 if(Roo.dd.DropZone){
18389     
18390 Roo.tree.TreeDropZone = function(tree, config){
18391     this.allowParentInsert = false;
18392     this.allowContainerDrop = false;
18393     this.appendOnly = false;
18394     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18395     this.tree = tree;
18396     this.lastInsertClass = "x-tree-no-status";
18397     this.dragOverData = {};
18398 };
18399
18400 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18401     ddGroup : "TreeDD",
18402     scroll:  true,
18403     
18404     expandDelay : 1000,
18405     
18406     expandNode : function(node){
18407         if(node.hasChildNodes() && !node.isExpanded()){
18408             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18409         }
18410     },
18411     
18412     queueExpand : function(node){
18413         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18414     },
18415     
18416     cancelExpand : function(){
18417         if(this.expandProcId){
18418             clearTimeout(this.expandProcId);
18419             this.expandProcId = false;
18420         }
18421     },
18422     
18423     isValidDropPoint : function(n, pt, dd, e, data){
18424         if(!n || !data){ return false; }
18425         var targetNode = n.node;
18426         var dropNode = data.node;
18427         // default drop rules
18428         if(!(targetNode && targetNode.isTarget && pt)){
18429             return false;
18430         }
18431         if(pt == "append" && targetNode.allowChildren === false){
18432             return false;
18433         }
18434         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18435             return false;
18436         }
18437         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18438             return false;
18439         }
18440         // reuse the object
18441         var overEvent = this.dragOverData;
18442         overEvent.tree = this.tree;
18443         overEvent.target = targetNode;
18444         overEvent.data = data;
18445         overEvent.point = pt;
18446         overEvent.source = dd;
18447         overEvent.rawEvent = e;
18448         overEvent.dropNode = dropNode;
18449         overEvent.cancel = false;  
18450         var result = this.tree.fireEvent("nodedragover", overEvent);
18451         return overEvent.cancel === false && result !== false;
18452     },
18453     
18454     getDropPoint : function(e, n, dd)
18455     {
18456         var tn = n.node;
18457         if(tn.isRoot){
18458             return tn.allowChildren !== false ? "append" : false; // always append for root
18459         }
18460         var dragEl = n.ddel;
18461         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18462         var y = Roo.lib.Event.getPageY(e);
18463         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18464         
18465         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18466         var noAppend = tn.allowChildren === false;
18467         if(this.appendOnly || tn.parentNode.allowChildren === false){
18468             return noAppend ? false : "append";
18469         }
18470         var noBelow = false;
18471         if(!this.allowParentInsert){
18472             noBelow = tn.hasChildNodes() && tn.isExpanded();
18473         }
18474         var q = (b - t) / (noAppend ? 2 : 3);
18475         if(y >= t && y < (t + q)){
18476             return "above";
18477         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18478             return "below";
18479         }else{
18480             return "append";
18481         }
18482     },
18483     
18484     onNodeEnter : function(n, dd, e, data)
18485     {
18486         this.cancelExpand();
18487     },
18488     
18489     onNodeOver : function(n, dd, e, data)
18490     {
18491        
18492         var pt = this.getDropPoint(e, n, dd);
18493         var node = n.node;
18494         
18495         // auto node expand check
18496         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18497             this.queueExpand(node);
18498         }else if(pt != "append"){
18499             this.cancelExpand();
18500         }
18501         
18502         // set the insert point style on the target node
18503         var returnCls = this.dropNotAllowed;
18504         if(this.isValidDropPoint(n, pt, dd, e, data)){
18505            if(pt){
18506                var el = n.ddel;
18507                var cls;
18508                if(pt == "above"){
18509                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18510                    cls = "x-tree-drag-insert-above";
18511                }else if(pt == "below"){
18512                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18513                    cls = "x-tree-drag-insert-below";
18514                }else{
18515                    returnCls = "x-tree-drop-ok-append";
18516                    cls = "x-tree-drag-append";
18517                }
18518                if(this.lastInsertClass != cls){
18519                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18520                    this.lastInsertClass = cls;
18521                }
18522            }
18523        }
18524        return returnCls;
18525     },
18526     
18527     onNodeOut : function(n, dd, e, data){
18528         
18529         this.cancelExpand();
18530         this.removeDropIndicators(n);
18531     },
18532     
18533     onNodeDrop : function(n, dd, e, data){
18534         var point = this.getDropPoint(e, n, dd);
18535         var targetNode = n.node;
18536         targetNode.ui.startDrop();
18537         if(!this.isValidDropPoint(n, point, dd, e, data)){
18538             targetNode.ui.endDrop();
18539             return false;
18540         }
18541         // first try to find the drop node
18542         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18543         var dropEvent = {
18544             tree : this.tree,
18545             target: targetNode,
18546             data: data,
18547             point: point,
18548             source: dd,
18549             rawEvent: e,
18550             dropNode: dropNode,
18551             cancel: !dropNode   
18552         };
18553         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18554         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18555             targetNode.ui.endDrop();
18556             return false;
18557         }
18558         // allow target changing
18559         targetNode = dropEvent.target;
18560         if(point == "append" && !targetNode.isExpanded()){
18561             targetNode.expand(false, null, function(){
18562                 this.completeDrop(dropEvent);
18563             }.createDelegate(this));
18564         }else{
18565             this.completeDrop(dropEvent);
18566         }
18567         return true;
18568     },
18569     
18570     completeDrop : function(de){
18571         var ns = de.dropNode, p = de.point, t = de.target;
18572         if(!(ns instanceof Array)){
18573             ns = [ns];
18574         }
18575         var n;
18576         for(var i = 0, len = ns.length; i < len; i++){
18577             n = ns[i];
18578             if(p == "above"){
18579                 t.parentNode.insertBefore(n, t);
18580             }else if(p == "below"){
18581                 t.parentNode.insertBefore(n, t.nextSibling);
18582             }else{
18583                 t.appendChild(n);
18584             }
18585         }
18586         n.ui.focus();
18587         if(this.tree.hlDrop){
18588             n.ui.highlight();
18589         }
18590         t.ui.endDrop();
18591         this.tree.fireEvent("nodedrop", de);
18592     },
18593     
18594     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18595         if(this.tree.hlDrop){
18596             dropNode.ui.focus();
18597             dropNode.ui.highlight();
18598         }
18599         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18600     },
18601     
18602     getTree : function(){
18603         return this.tree;
18604     },
18605     
18606     removeDropIndicators : function(n){
18607         if(n && n.ddel){
18608             var el = n.ddel;
18609             Roo.fly(el).removeClass([
18610                     "x-tree-drag-insert-above",
18611                     "x-tree-drag-insert-below",
18612                     "x-tree-drag-append"]);
18613             this.lastInsertClass = "_noclass";
18614         }
18615     },
18616     
18617     beforeDragDrop : function(target, e, id){
18618         this.cancelExpand();
18619         return true;
18620     },
18621     
18622     afterRepair : function(data){
18623         if(data && Roo.enableFx){
18624             data.node.ui.highlight();
18625         }
18626         this.hideProxy();
18627     } 
18628     
18629 });
18630
18631 }
18632 /*
18633  * Based on:
18634  * Ext JS Library 1.1.1
18635  * Copyright(c) 2006-2007, Ext JS, LLC.
18636  *
18637  * Originally Released Under LGPL - original licence link has changed is not relivant.
18638  *
18639  * Fork - LGPL
18640  * <script type="text/javascript">
18641  */
18642  
18643
18644 if(Roo.dd.DragZone){
18645 Roo.tree.TreeDragZone = function(tree, config){
18646     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18647     this.tree = tree;
18648 };
18649
18650 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18651     ddGroup : "TreeDD",
18652    
18653     onBeforeDrag : function(data, e){
18654         var n = data.node;
18655         return n && n.draggable && !n.disabled;
18656     },
18657      
18658     
18659     onInitDrag : function(e){
18660         var data = this.dragData;
18661         this.tree.getSelectionModel().select(data.node);
18662         this.proxy.update("");
18663         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18664         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18665     },
18666     
18667     getRepairXY : function(e, data){
18668         return data.node.ui.getDDRepairXY();
18669     },
18670     
18671     onEndDrag : function(data, e){
18672         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18673         
18674         
18675     },
18676     
18677     onValidDrop : function(dd, e, id){
18678         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18679         this.hideProxy();
18680     },
18681     
18682     beforeInvalidDrop : function(e, id){
18683         // this scrolls the original position back into view
18684         var sm = this.tree.getSelectionModel();
18685         sm.clearSelections();
18686         sm.select(this.dragData.node);
18687     }
18688 });
18689 }/*
18690  * Based on:
18691  * Ext JS Library 1.1.1
18692  * Copyright(c) 2006-2007, Ext JS, LLC.
18693  *
18694  * Originally Released Under LGPL - original licence link has changed is not relivant.
18695  *
18696  * Fork - LGPL
18697  * <script type="text/javascript">
18698  */
18699 /**
18700  * @class Roo.tree.TreeEditor
18701  * @extends Roo.Editor
18702  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18703  * as the editor field.
18704  * @constructor
18705  * @param {Object} config (used to be the tree panel.)
18706  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18707  * 
18708  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18709  * @cfg {Roo.form.TextField|Object} field The field configuration
18710  *
18711  * 
18712  */
18713 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18714     var tree = config;
18715     var field;
18716     if (oldconfig) { // old style..
18717         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18718     } else {
18719         // new style..
18720         tree = config.tree;
18721         config.field = config.field  || {};
18722         config.field.xtype = 'TextField';
18723         field = Roo.factory(config.field, Roo.form);
18724     }
18725     config = config || {};
18726     
18727     
18728     this.addEvents({
18729         /**
18730          * @event beforenodeedit
18731          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18732          * false from the handler of this event.
18733          * @param {Editor} this
18734          * @param {Roo.tree.Node} node 
18735          */
18736         "beforenodeedit" : true
18737     });
18738     
18739     //Roo.log(config);
18740     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18741
18742     this.tree = tree;
18743
18744     tree.on('beforeclick', this.beforeNodeClick, this);
18745     tree.getTreeEl().on('mousedown', this.hide, this);
18746     this.on('complete', this.updateNode, this);
18747     this.on('beforestartedit', this.fitToTree, this);
18748     this.on('startedit', this.bindScroll, this, {delay:10});
18749     this.on('specialkey', this.onSpecialKey, this);
18750 };
18751
18752 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18753     /**
18754      * @cfg {String} alignment
18755      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18756      */
18757     alignment: "l-l",
18758     // inherit
18759     autoSize: false,
18760     /**
18761      * @cfg {Boolean} hideEl
18762      * True to hide the bound element while the editor is displayed (defaults to false)
18763      */
18764     hideEl : false,
18765     /**
18766      * @cfg {String} cls
18767      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18768      */
18769     cls: "x-small-editor x-tree-editor",
18770     /**
18771      * @cfg {Boolean} shim
18772      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18773      */
18774     shim:false,
18775     // inherit
18776     shadow:"frame",
18777     /**
18778      * @cfg {Number} maxWidth
18779      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18780      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18781      * scroll and client offsets into account prior to each edit.
18782      */
18783     maxWidth: 250,
18784
18785     editDelay : 350,
18786
18787     // private
18788     fitToTree : function(ed, el){
18789         var td = this.tree.getTreeEl().dom, nd = el.dom;
18790         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18791             td.scrollLeft = nd.offsetLeft;
18792         }
18793         var w = Math.min(
18794                 this.maxWidth,
18795                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18796         this.setSize(w, '');
18797         
18798         return this.fireEvent('beforenodeedit', this, this.editNode);
18799         
18800     },
18801
18802     // private
18803     triggerEdit : function(node){
18804         this.completeEdit();
18805         this.editNode = node;
18806         this.startEdit(node.ui.textNode, node.text);
18807     },
18808
18809     // private
18810     bindScroll : function(){
18811         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18812     },
18813
18814     // private
18815     beforeNodeClick : function(node, e){
18816         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18817         this.lastClick = new Date();
18818         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18819             e.stopEvent();
18820             this.triggerEdit(node);
18821             return false;
18822         }
18823         return true;
18824     },
18825
18826     // private
18827     updateNode : function(ed, value){
18828         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18829         this.editNode.setText(value);
18830     },
18831
18832     // private
18833     onHide : function(){
18834         Roo.tree.TreeEditor.superclass.onHide.call(this);
18835         if(this.editNode){
18836             this.editNode.ui.focus();
18837         }
18838     },
18839
18840     // private
18841     onSpecialKey : function(field, e){
18842         var k = e.getKey();
18843         if(k == e.ESC){
18844             e.stopEvent();
18845             this.cancelEdit();
18846         }else if(k == e.ENTER && !e.hasModifier()){
18847             e.stopEvent();
18848             this.completeEdit();
18849         }
18850     }
18851 });//<Script type="text/javascript">
18852 /*
18853  * Based on:
18854  * Ext JS Library 1.1.1
18855  * Copyright(c) 2006-2007, Ext JS, LLC.
18856  *
18857  * Originally Released Under LGPL - original licence link has changed is not relivant.
18858  *
18859  * Fork - LGPL
18860  * <script type="text/javascript">
18861  */
18862  
18863 /**
18864  * Not documented??? - probably should be...
18865  */
18866
18867 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18868     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18869     
18870     renderElements : function(n, a, targetNode, bulkRender){
18871         //consel.log("renderElements?");
18872         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18873
18874         var t = n.getOwnerTree();
18875         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18876         
18877         var cols = t.columns;
18878         var bw = t.borderWidth;
18879         var c = cols[0];
18880         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18881          var cb = typeof a.checked == "boolean";
18882         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18883         var colcls = 'x-t-' + tid + '-c0';
18884         var buf = [
18885             '<li class="x-tree-node">',
18886             
18887                 
18888                 '<div class="x-tree-node-el ', a.cls,'">',
18889                     // extran...
18890                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18891                 
18892                 
18893                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18894                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18895                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18896                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18897                            (a.iconCls ? ' '+a.iconCls : ''),
18898                            '" unselectable="on" />',
18899                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18900                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18901                              
18902                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18903                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18904                             '<span unselectable="on" qtip="' + tx + '">',
18905                              tx,
18906                              '</span></a>' ,
18907                     '</div>',
18908                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18909                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18910                  ];
18911         for(var i = 1, len = cols.length; i < len; i++){
18912             c = cols[i];
18913             colcls = 'x-t-' + tid + '-c' +i;
18914             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18915             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18916                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18917                       "</div>");
18918          }
18919          
18920          buf.push(
18921             '</a>',
18922             '<div class="x-clear"></div></div>',
18923             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18924             "</li>");
18925         
18926         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18927             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18928                                 n.nextSibling.ui.getEl(), buf.join(""));
18929         }else{
18930             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18931         }
18932         var el = this.wrap.firstChild;
18933         this.elRow = el;
18934         this.elNode = el.firstChild;
18935         this.ranchor = el.childNodes[1];
18936         this.ctNode = this.wrap.childNodes[1];
18937         var cs = el.firstChild.childNodes;
18938         this.indentNode = cs[0];
18939         this.ecNode = cs[1];
18940         this.iconNode = cs[2];
18941         var index = 3;
18942         if(cb){
18943             this.checkbox = cs[3];
18944             index++;
18945         }
18946         this.anchor = cs[index];
18947         
18948         this.textNode = cs[index].firstChild;
18949         
18950         //el.on("click", this.onClick, this);
18951         //el.on("dblclick", this.onDblClick, this);
18952         
18953         
18954        // console.log(this);
18955     },
18956     initEvents : function(){
18957         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18958         
18959             
18960         var a = this.ranchor;
18961
18962         var el = Roo.get(a);
18963
18964         if(Roo.isOpera){ // opera render bug ignores the CSS
18965             el.setStyle("text-decoration", "none");
18966         }
18967
18968         el.on("click", this.onClick, this);
18969         el.on("dblclick", this.onDblClick, this);
18970         el.on("contextmenu", this.onContextMenu, this);
18971         
18972     },
18973     
18974     /*onSelectedChange : function(state){
18975         if(state){
18976             this.focus();
18977             this.addClass("x-tree-selected");
18978         }else{
18979             //this.blur();
18980             this.removeClass("x-tree-selected");
18981         }
18982     },*/
18983     addClass : function(cls){
18984         if(this.elRow){
18985             Roo.fly(this.elRow).addClass(cls);
18986         }
18987         
18988     },
18989     
18990     
18991     removeClass : function(cls){
18992         if(this.elRow){
18993             Roo.fly(this.elRow).removeClass(cls);
18994         }
18995     }
18996
18997     
18998     
18999 });//<Script type="text/javascript">
19000
19001 /*
19002  * Based on:
19003  * Ext JS Library 1.1.1
19004  * Copyright(c) 2006-2007, Ext JS, LLC.
19005  *
19006  * Originally Released Under LGPL - original licence link has changed is not relivant.
19007  *
19008  * Fork - LGPL
19009  * <script type="text/javascript">
19010  */
19011  
19012
19013 /**
19014  * @class Roo.tree.ColumnTree
19015  * @extends Roo.data.TreePanel
19016  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19017  * @cfg {int} borderWidth  compined right/left border allowance
19018  * @constructor
19019  * @param {String/HTMLElement/Element} el The container element
19020  * @param {Object} config
19021  */
19022 Roo.tree.ColumnTree =  function(el, config)
19023 {
19024    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19025    this.addEvents({
19026         /**
19027         * @event resize
19028         * Fire this event on a container when it resizes
19029         * @param {int} w Width
19030         * @param {int} h Height
19031         */
19032        "resize" : true
19033     });
19034     this.on('resize', this.onResize, this);
19035 };
19036
19037 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19038     //lines:false,
19039     
19040     
19041     borderWidth: Roo.isBorderBox ? 0 : 2, 
19042     headEls : false,
19043     
19044     render : function(){
19045         // add the header.....
19046        
19047         Roo.tree.ColumnTree.superclass.render.apply(this);
19048         
19049         this.el.addClass('x-column-tree');
19050         
19051         this.headers = this.el.createChild(
19052             {cls:'x-tree-headers'},this.innerCt.dom);
19053    
19054         var cols = this.columns, c;
19055         var totalWidth = 0;
19056         this.headEls = [];
19057         var  len = cols.length;
19058         for(var i = 0; i < len; i++){
19059              c = cols[i];
19060              totalWidth += c.width;
19061             this.headEls.push(this.headers.createChild({
19062                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19063                  cn: {
19064                      cls:'x-tree-hd-text',
19065                      html: c.header
19066                  },
19067                  style:'width:'+(c.width-this.borderWidth)+'px;'
19068              }));
19069         }
19070         this.headers.createChild({cls:'x-clear'});
19071         // prevent floats from wrapping when clipped
19072         this.headers.setWidth(totalWidth);
19073         //this.innerCt.setWidth(totalWidth);
19074         this.innerCt.setStyle({ overflow: 'auto' });
19075         this.onResize(this.width, this.height);
19076              
19077         
19078     },
19079     onResize : function(w,h)
19080     {
19081         this.height = h;
19082         this.width = w;
19083         // resize cols..
19084         this.innerCt.setWidth(this.width);
19085         this.innerCt.setHeight(this.height-20);
19086         
19087         // headers...
19088         var cols = this.columns, c;
19089         var totalWidth = 0;
19090         var expEl = false;
19091         var len = cols.length;
19092         for(var i = 0; i < len; i++){
19093             c = cols[i];
19094             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19095                 // it's the expander..
19096                 expEl  = this.headEls[i];
19097                 continue;
19098             }
19099             totalWidth += c.width;
19100             
19101         }
19102         if (expEl) {
19103             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19104         }
19105         this.headers.setWidth(w-20);
19106
19107         
19108         
19109         
19110     }
19111 });
19112 /*
19113  * Based on:
19114  * Ext JS Library 1.1.1
19115  * Copyright(c) 2006-2007, Ext JS, LLC.
19116  *
19117  * Originally Released Under LGPL - original licence link has changed is not relivant.
19118  *
19119  * Fork - LGPL
19120  * <script type="text/javascript">
19121  */
19122  
19123 /**
19124  * @class Roo.menu.Menu
19125  * @extends Roo.util.Observable
19126  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19127  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19128  * @constructor
19129  * Creates a new Menu
19130  * @param {Object} config Configuration options
19131  */
19132 Roo.menu.Menu = function(config){
19133     Roo.apply(this, config);
19134     this.id = this.id || Roo.id();
19135     this.addEvents({
19136         /**
19137          * @event beforeshow
19138          * Fires before this menu is displayed
19139          * @param {Roo.menu.Menu} this
19140          */
19141         beforeshow : true,
19142         /**
19143          * @event beforehide
19144          * Fires before this menu is hidden
19145          * @param {Roo.menu.Menu} this
19146          */
19147         beforehide : true,
19148         /**
19149          * @event show
19150          * Fires after this menu is displayed
19151          * @param {Roo.menu.Menu} this
19152          */
19153         show : true,
19154         /**
19155          * @event hide
19156          * Fires after this menu is hidden
19157          * @param {Roo.menu.Menu} this
19158          */
19159         hide : true,
19160         /**
19161          * @event click
19162          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19163          * @param {Roo.menu.Menu} this
19164          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19165          * @param {Roo.EventObject} e
19166          */
19167         click : true,
19168         /**
19169          * @event mouseover
19170          * Fires when the mouse is hovering over this menu
19171          * @param {Roo.menu.Menu} this
19172          * @param {Roo.EventObject} e
19173          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19174          */
19175         mouseover : true,
19176         /**
19177          * @event mouseout
19178          * Fires when the mouse exits this menu
19179          * @param {Roo.menu.Menu} this
19180          * @param {Roo.EventObject} e
19181          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19182          */
19183         mouseout : true,
19184         /**
19185          * @event itemclick
19186          * Fires when a menu item contained in this menu is clicked
19187          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19188          * @param {Roo.EventObject} e
19189          */
19190         itemclick: true
19191     });
19192     if (this.registerMenu) {
19193         Roo.menu.MenuMgr.register(this);
19194     }
19195     
19196     var mis = this.items;
19197     this.items = new Roo.util.MixedCollection();
19198     if(mis){
19199         this.add.apply(this, mis);
19200     }
19201 };
19202
19203 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19204     /**
19205      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19206      */
19207     minWidth : 120,
19208     /**
19209      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19210      * for bottom-right shadow (defaults to "sides")
19211      */
19212     shadow : "sides",
19213     /**
19214      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19215      * this menu (defaults to "tl-tr?")
19216      */
19217     subMenuAlign : "tl-tr?",
19218     /**
19219      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19220      * relative to its element of origin (defaults to "tl-bl?")
19221      */
19222     defaultAlign : "tl-bl?",
19223     /**
19224      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19225      */
19226     allowOtherMenus : false,
19227     /**
19228      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19229      */
19230     registerMenu : true,
19231
19232     hidden:true,
19233
19234     // private
19235     render : function(){
19236         if(this.el){
19237             return;
19238         }
19239         var el = this.el = new Roo.Layer({
19240             cls: "x-menu",
19241             shadow:this.shadow,
19242             constrain: false,
19243             parentEl: this.parentEl || document.body,
19244             zindex:15000
19245         });
19246
19247         this.keyNav = new Roo.menu.MenuNav(this);
19248
19249         if(this.plain){
19250             el.addClass("x-menu-plain");
19251         }
19252         if(this.cls){
19253             el.addClass(this.cls);
19254         }
19255         // generic focus element
19256         this.focusEl = el.createChild({
19257             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19258         });
19259         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19260         //disabling touch- as it's causing issues ..
19261         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19262         ul.on('click'   , this.onClick, this);
19263         
19264         
19265         ul.on("mouseover", this.onMouseOver, this);
19266         ul.on("mouseout", this.onMouseOut, this);
19267         this.items.each(function(item){
19268             if (item.hidden) {
19269                 return;
19270             }
19271             
19272             var li = document.createElement("li");
19273             li.className = "x-menu-list-item";
19274             ul.dom.appendChild(li);
19275             item.render(li, this);
19276         }, this);
19277         this.ul = ul;
19278         this.autoWidth();
19279     },
19280
19281     // private
19282     autoWidth : function(){
19283         var el = this.el, ul = this.ul;
19284         if(!el){
19285             return;
19286         }
19287         var w = this.width;
19288         if(w){
19289             el.setWidth(w);
19290         }else if(Roo.isIE){
19291             el.setWidth(this.minWidth);
19292             var t = el.dom.offsetWidth; // force recalc
19293             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19294         }
19295     },
19296
19297     // private
19298     delayAutoWidth : function(){
19299         if(this.rendered){
19300             if(!this.awTask){
19301                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19302             }
19303             this.awTask.delay(20);
19304         }
19305     },
19306
19307     // private
19308     findTargetItem : function(e){
19309         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19310         if(t && t.menuItemId){
19311             return this.items.get(t.menuItemId);
19312         }
19313     },
19314
19315     // private
19316     onClick : function(e){
19317         Roo.log("menu.onClick");
19318         var t = this.findTargetItem(e);
19319         if(!t){
19320             return;
19321         }
19322         Roo.log(e);
19323         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19324             if(t == this.activeItem && t.shouldDeactivate(e)){
19325                 this.activeItem.deactivate();
19326                 delete this.activeItem;
19327                 return;
19328             }
19329             if(t.canActivate){
19330                 this.setActiveItem(t, true);
19331             }
19332             return;
19333             
19334             
19335         }
19336         
19337         t.onClick(e);
19338         this.fireEvent("click", this, t, e);
19339     },
19340
19341     // private
19342     setActiveItem : function(item, autoExpand){
19343         if(item != this.activeItem){
19344             if(this.activeItem){
19345                 this.activeItem.deactivate();
19346             }
19347             this.activeItem = item;
19348             item.activate(autoExpand);
19349         }else if(autoExpand){
19350             item.expandMenu();
19351         }
19352     },
19353
19354     // private
19355     tryActivate : function(start, step){
19356         var items = this.items;
19357         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19358             var item = items.get(i);
19359             if(!item.disabled && item.canActivate){
19360                 this.setActiveItem(item, false);
19361                 return item;
19362             }
19363         }
19364         return false;
19365     },
19366
19367     // private
19368     onMouseOver : function(e){
19369         var t;
19370         if(t = this.findTargetItem(e)){
19371             if(t.canActivate && !t.disabled){
19372                 this.setActiveItem(t, true);
19373             }
19374         }
19375         this.fireEvent("mouseover", this, e, t);
19376     },
19377
19378     // private
19379     onMouseOut : function(e){
19380         var t;
19381         if(t = this.findTargetItem(e)){
19382             if(t == this.activeItem && t.shouldDeactivate(e)){
19383                 this.activeItem.deactivate();
19384                 delete this.activeItem;
19385             }
19386         }
19387         this.fireEvent("mouseout", this, e, t);
19388     },
19389
19390     /**
19391      * Read-only.  Returns true if the menu is currently displayed, else false.
19392      * @type Boolean
19393      */
19394     isVisible : function(){
19395         return this.el && !this.hidden;
19396     },
19397
19398     /**
19399      * Displays this menu relative to another element
19400      * @param {String/HTMLElement/Roo.Element} element The element to align to
19401      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19402      * the element (defaults to this.defaultAlign)
19403      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19404      */
19405     show : function(el, pos, parentMenu){
19406         this.parentMenu = parentMenu;
19407         if(!this.el){
19408             this.render();
19409         }
19410         this.fireEvent("beforeshow", this);
19411         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19412     },
19413
19414     /**
19415      * Displays this menu at a specific xy position
19416      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19417      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19418      */
19419     showAt : function(xy, parentMenu, /* private: */_e){
19420         this.parentMenu = parentMenu;
19421         if(!this.el){
19422             this.render();
19423         }
19424         if(_e !== false){
19425             this.fireEvent("beforeshow", this);
19426             xy = this.el.adjustForConstraints(xy);
19427         }
19428         this.el.setXY(xy);
19429         this.el.show();
19430         this.hidden = false;
19431         this.focus();
19432         this.fireEvent("show", this);
19433     },
19434
19435     focus : function(){
19436         if(!this.hidden){
19437             this.doFocus.defer(50, this);
19438         }
19439     },
19440
19441     doFocus : function(){
19442         if(!this.hidden){
19443             this.focusEl.focus();
19444         }
19445     },
19446
19447     /**
19448      * Hides this menu and optionally all parent menus
19449      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19450      */
19451     hide : function(deep){
19452         if(this.el && this.isVisible()){
19453             this.fireEvent("beforehide", this);
19454             if(this.activeItem){
19455                 this.activeItem.deactivate();
19456                 this.activeItem = null;
19457             }
19458             this.el.hide();
19459             this.hidden = true;
19460             this.fireEvent("hide", this);
19461         }
19462         if(deep === true && this.parentMenu){
19463             this.parentMenu.hide(true);
19464         }
19465     },
19466
19467     /**
19468      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19469      * Any of the following are valid:
19470      * <ul>
19471      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19472      * <li>An HTMLElement object which will be converted to a menu item</li>
19473      * <li>A menu item config object that will be created as a new menu item</li>
19474      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19475      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19476      * </ul>
19477      * Usage:
19478      * <pre><code>
19479 // Create the menu
19480 var menu = new Roo.menu.Menu();
19481
19482 // Create a menu item to add by reference
19483 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19484
19485 // Add a bunch of items at once using different methods.
19486 // Only the last item added will be returned.
19487 var item = menu.add(
19488     menuItem,                // add existing item by ref
19489     'Dynamic Item',          // new TextItem
19490     '-',                     // new separator
19491     { text: 'Config Item' }  // new item by config
19492 );
19493 </code></pre>
19494      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19495      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19496      */
19497     add : function(){
19498         var a = arguments, l = a.length, item;
19499         for(var i = 0; i < l; i++){
19500             var el = a[i];
19501             if ((typeof(el) == "object") && el.xtype && el.xns) {
19502                 el = Roo.factory(el, Roo.menu);
19503             }
19504             
19505             if(el.render){ // some kind of Item
19506                 item = this.addItem(el);
19507             }else if(typeof el == "string"){ // string
19508                 if(el == "separator" || el == "-"){
19509                     item = this.addSeparator();
19510                 }else{
19511                     item = this.addText(el);
19512                 }
19513             }else if(el.tagName || el.el){ // element
19514                 item = this.addElement(el);
19515             }else if(typeof el == "object"){ // must be menu item config?
19516                 item = this.addMenuItem(el);
19517             }
19518         }
19519         return item;
19520     },
19521
19522     /**
19523      * Returns this menu's underlying {@link Roo.Element} object
19524      * @return {Roo.Element} The element
19525      */
19526     getEl : function(){
19527         if(!this.el){
19528             this.render();
19529         }
19530         return this.el;
19531     },
19532
19533     /**
19534      * Adds a separator bar to the menu
19535      * @return {Roo.menu.Item} The menu item that was added
19536      */
19537     addSeparator : function(){
19538         return this.addItem(new Roo.menu.Separator());
19539     },
19540
19541     /**
19542      * Adds an {@link Roo.Element} object to the menu
19543      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19544      * @return {Roo.menu.Item} The menu item that was added
19545      */
19546     addElement : function(el){
19547         return this.addItem(new Roo.menu.BaseItem(el));
19548     },
19549
19550     /**
19551      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19552      * @param {Roo.menu.Item} item The menu item to add
19553      * @return {Roo.menu.Item} The menu item that was added
19554      */
19555     addItem : function(item){
19556         this.items.add(item);
19557         if(this.ul){
19558             var li = document.createElement("li");
19559             li.className = "x-menu-list-item";
19560             this.ul.dom.appendChild(li);
19561             item.render(li, this);
19562             this.delayAutoWidth();
19563         }
19564         return item;
19565     },
19566
19567     /**
19568      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19569      * @param {Object} config A MenuItem config object
19570      * @return {Roo.menu.Item} The menu item that was added
19571      */
19572     addMenuItem : function(config){
19573         if(!(config instanceof Roo.menu.Item)){
19574             if(typeof config.checked == "boolean"){ // must be check menu item config?
19575                 config = new Roo.menu.CheckItem(config);
19576             }else{
19577                 config = new Roo.menu.Item(config);
19578             }
19579         }
19580         return this.addItem(config);
19581     },
19582
19583     /**
19584      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19585      * @param {String} text The text to display in the menu item
19586      * @return {Roo.menu.Item} The menu item that was added
19587      */
19588     addText : function(text){
19589         return this.addItem(new Roo.menu.TextItem({ text : text }));
19590     },
19591
19592     /**
19593      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19594      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19595      * @param {Roo.menu.Item} item The menu item to add
19596      * @return {Roo.menu.Item} The menu item that was added
19597      */
19598     insert : function(index, item){
19599         this.items.insert(index, item);
19600         if(this.ul){
19601             var li = document.createElement("li");
19602             li.className = "x-menu-list-item";
19603             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19604             item.render(li, this);
19605             this.delayAutoWidth();
19606         }
19607         return item;
19608     },
19609
19610     /**
19611      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19612      * @param {Roo.menu.Item} item The menu item to remove
19613      */
19614     remove : function(item){
19615         this.items.removeKey(item.id);
19616         item.destroy();
19617     },
19618
19619     /**
19620      * Removes and destroys all items in the menu
19621      */
19622     removeAll : function(){
19623         var f;
19624         while(f = this.items.first()){
19625             this.remove(f);
19626         }
19627     }
19628 });
19629
19630 // MenuNav is a private utility class used internally by the Menu
19631 Roo.menu.MenuNav = function(menu){
19632     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19633     this.scope = this.menu = menu;
19634 };
19635
19636 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19637     doRelay : function(e, h){
19638         var k = e.getKey();
19639         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19640             this.menu.tryActivate(0, 1);
19641             return false;
19642         }
19643         return h.call(this.scope || this, e, this.menu);
19644     },
19645
19646     up : function(e, m){
19647         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19648             m.tryActivate(m.items.length-1, -1);
19649         }
19650     },
19651
19652     down : function(e, m){
19653         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19654             m.tryActivate(0, 1);
19655         }
19656     },
19657
19658     right : function(e, m){
19659         if(m.activeItem){
19660             m.activeItem.expandMenu(true);
19661         }
19662     },
19663
19664     left : function(e, m){
19665         m.hide();
19666         if(m.parentMenu && m.parentMenu.activeItem){
19667             m.parentMenu.activeItem.activate();
19668         }
19669     },
19670
19671     enter : function(e, m){
19672         if(m.activeItem){
19673             e.stopPropagation();
19674             m.activeItem.onClick(e);
19675             m.fireEvent("click", this, m.activeItem);
19676             return true;
19677         }
19678     }
19679 });/*
19680  * Based on:
19681  * Ext JS Library 1.1.1
19682  * Copyright(c) 2006-2007, Ext JS, LLC.
19683  *
19684  * Originally Released Under LGPL - original licence link has changed is not relivant.
19685  *
19686  * Fork - LGPL
19687  * <script type="text/javascript">
19688  */
19689  
19690 /**
19691  * @class Roo.menu.MenuMgr
19692  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19693  * @singleton
19694  */
19695 Roo.menu.MenuMgr = function(){
19696    var menus, active, groups = {}, attached = false, lastShow = new Date();
19697
19698    // private - called when first menu is created
19699    function init(){
19700        menus = {};
19701        active = new Roo.util.MixedCollection();
19702        Roo.get(document).addKeyListener(27, function(){
19703            if(active.length > 0){
19704                hideAll();
19705            }
19706        });
19707    }
19708
19709    // private
19710    function hideAll(){
19711        if(active && active.length > 0){
19712            var c = active.clone();
19713            c.each(function(m){
19714                m.hide();
19715            });
19716        }
19717    }
19718
19719    // private
19720    function onHide(m){
19721        active.remove(m);
19722        if(active.length < 1){
19723            Roo.get(document).un("mousedown", onMouseDown);
19724            attached = false;
19725        }
19726    }
19727
19728    // private
19729    function onShow(m){
19730        var last = active.last();
19731        lastShow = new Date();
19732        active.add(m);
19733        if(!attached){
19734            Roo.get(document).on("mousedown", onMouseDown);
19735            attached = true;
19736        }
19737        if(m.parentMenu){
19738           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19739           m.parentMenu.activeChild = m;
19740        }else if(last && last.isVisible()){
19741           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19742        }
19743    }
19744
19745    // private
19746    function onBeforeHide(m){
19747        if(m.activeChild){
19748            m.activeChild.hide();
19749        }
19750        if(m.autoHideTimer){
19751            clearTimeout(m.autoHideTimer);
19752            delete m.autoHideTimer;
19753        }
19754    }
19755
19756    // private
19757    function onBeforeShow(m){
19758        var pm = m.parentMenu;
19759        if(!pm && !m.allowOtherMenus){
19760            hideAll();
19761        }else if(pm && pm.activeChild && active != m){
19762            pm.activeChild.hide();
19763        }
19764    }
19765
19766    // private
19767    function onMouseDown(e){
19768        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19769            hideAll();
19770        }
19771    }
19772
19773    // private
19774    function onBeforeCheck(mi, state){
19775        if(state){
19776            var g = groups[mi.group];
19777            for(var i = 0, l = g.length; i < l; i++){
19778                if(g[i] != mi){
19779                    g[i].setChecked(false);
19780                }
19781            }
19782        }
19783    }
19784
19785    return {
19786
19787        /**
19788         * Hides all menus that are currently visible
19789         */
19790        hideAll : function(){
19791             hideAll();  
19792        },
19793
19794        // private
19795        register : function(menu){
19796            if(!menus){
19797                init();
19798            }
19799            menus[menu.id] = menu;
19800            menu.on("beforehide", onBeforeHide);
19801            menu.on("hide", onHide);
19802            menu.on("beforeshow", onBeforeShow);
19803            menu.on("show", onShow);
19804            var g = menu.group;
19805            if(g && menu.events["checkchange"]){
19806                if(!groups[g]){
19807                    groups[g] = [];
19808                }
19809                groups[g].push(menu);
19810                menu.on("checkchange", onCheck);
19811            }
19812        },
19813
19814         /**
19815          * Returns a {@link Roo.menu.Menu} object
19816          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19817          * be used to generate and return a new Menu instance.
19818          */
19819        get : function(menu){
19820            if(typeof menu == "string"){ // menu id
19821                return menus[menu];
19822            }else if(menu.events){  // menu instance
19823                return menu;
19824            }else if(typeof menu.length == 'number'){ // array of menu items?
19825                return new Roo.menu.Menu({items:menu});
19826            }else{ // otherwise, must be a config
19827                return new Roo.menu.Menu(menu);
19828            }
19829        },
19830
19831        // private
19832        unregister : function(menu){
19833            delete menus[menu.id];
19834            menu.un("beforehide", onBeforeHide);
19835            menu.un("hide", onHide);
19836            menu.un("beforeshow", onBeforeShow);
19837            menu.un("show", onShow);
19838            var g = menu.group;
19839            if(g && menu.events["checkchange"]){
19840                groups[g].remove(menu);
19841                menu.un("checkchange", onCheck);
19842            }
19843        },
19844
19845        // private
19846        registerCheckable : function(menuItem){
19847            var g = menuItem.group;
19848            if(g){
19849                if(!groups[g]){
19850                    groups[g] = [];
19851                }
19852                groups[g].push(menuItem);
19853                menuItem.on("beforecheckchange", onBeforeCheck);
19854            }
19855        },
19856
19857        // private
19858        unregisterCheckable : function(menuItem){
19859            var g = menuItem.group;
19860            if(g){
19861                groups[g].remove(menuItem);
19862                menuItem.un("beforecheckchange", onBeforeCheck);
19863            }
19864        }
19865    };
19866 }();/*
19867  * Based on:
19868  * Ext JS Library 1.1.1
19869  * Copyright(c) 2006-2007, Ext JS, LLC.
19870  *
19871  * Originally Released Under LGPL - original licence link has changed is not relivant.
19872  *
19873  * Fork - LGPL
19874  * <script type="text/javascript">
19875  */
19876  
19877
19878 /**
19879  * @class Roo.menu.BaseItem
19880  * @extends Roo.Component
19881  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19882  * management and base configuration options shared by all menu components.
19883  * @constructor
19884  * Creates a new BaseItem
19885  * @param {Object} config Configuration options
19886  */
19887 Roo.menu.BaseItem = function(config){
19888     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19889
19890     this.addEvents({
19891         /**
19892          * @event click
19893          * Fires when this item is clicked
19894          * @param {Roo.menu.BaseItem} this
19895          * @param {Roo.EventObject} e
19896          */
19897         click: true,
19898         /**
19899          * @event activate
19900          * Fires when this item is activated
19901          * @param {Roo.menu.BaseItem} this
19902          */
19903         activate : true,
19904         /**
19905          * @event deactivate
19906          * Fires when this item is deactivated
19907          * @param {Roo.menu.BaseItem} this
19908          */
19909         deactivate : true
19910     });
19911
19912     if(this.handler){
19913         this.on("click", this.handler, this.scope, true);
19914     }
19915 };
19916
19917 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19918     /**
19919      * @cfg {Function} handler
19920      * A function that will handle the click event of this menu item (defaults to undefined)
19921      */
19922     /**
19923      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19924      */
19925     canActivate : false,
19926     
19927      /**
19928      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19929      */
19930     hidden: false,
19931     
19932     /**
19933      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19934      */
19935     activeClass : "x-menu-item-active",
19936     /**
19937      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19938      */
19939     hideOnClick : true,
19940     /**
19941      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19942      */
19943     hideDelay : 100,
19944
19945     // private
19946     ctype: "Roo.menu.BaseItem",
19947
19948     // private
19949     actionMode : "container",
19950
19951     // private
19952     render : function(container, parentMenu){
19953         this.parentMenu = parentMenu;
19954         Roo.menu.BaseItem.superclass.render.call(this, container);
19955         this.container.menuItemId = this.id;
19956     },
19957
19958     // private
19959     onRender : function(container, position){
19960         this.el = Roo.get(this.el);
19961         container.dom.appendChild(this.el.dom);
19962     },
19963
19964     // private
19965     onClick : function(e){
19966         if(!this.disabled && this.fireEvent("click", this, e) !== false
19967                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19968             this.handleClick(e);
19969         }else{
19970             e.stopEvent();
19971         }
19972     },
19973
19974     // private
19975     activate : function(){
19976         if(this.disabled){
19977             return false;
19978         }
19979         var li = this.container;
19980         li.addClass(this.activeClass);
19981         this.region = li.getRegion().adjust(2, 2, -2, -2);
19982         this.fireEvent("activate", this);
19983         return true;
19984     },
19985
19986     // private
19987     deactivate : function(){
19988         this.container.removeClass(this.activeClass);
19989         this.fireEvent("deactivate", this);
19990     },
19991
19992     // private
19993     shouldDeactivate : function(e){
19994         return !this.region || !this.region.contains(e.getPoint());
19995     },
19996
19997     // private
19998     handleClick : function(e){
19999         if(this.hideOnClick){
20000             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20001         }
20002     },
20003
20004     // private
20005     expandMenu : function(autoActivate){
20006         // do nothing
20007     },
20008
20009     // private
20010     hideMenu : function(){
20011         // do nothing
20012     }
20013 });/*
20014  * Based on:
20015  * Ext JS Library 1.1.1
20016  * Copyright(c) 2006-2007, Ext JS, LLC.
20017  *
20018  * Originally Released Under LGPL - original licence link has changed is not relivant.
20019  *
20020  * Fork - LGPL
20021  * <script type="text/javascript">
20022  */
20023  
20024 /**
20025  * @class Roo.menu.Adapter
20026  * @extends Roo.menu.BaseItem
20027  * 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.
20028  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20029  * @constructor
20030  * Creates a new Adapter
20031  * @param {Object} config Configuration options
20032  */
20033 Roo.menu.Adapter = function(component, config){
20034     Roo.menu.Adapter.superclass.constructor.call(this, config);
20035     this.component = component;
20036 };
20037 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20038     // private
20039     canActivate : true,
20040
20041     // private
20042     onRender : function(container, position){
20043         this.component.render(container);
20044         this.el = this.component.getEl();
20045     },
20046
20047     // private
20048     activate : function(){
20049         if(this.disabled){
20050             return false;
20051         }
20052         this.component.focus();
20053         this.fireEvent("activate", this);
20054         return true;
20055     },
20056
20057     // private
20058     deactivate : function(){
20059         this.fireEvent("deactivate", this);
20060     },
20061
20062     // private
20063     disable : function(){
20064         this.component.disable();
20065         Roo.menu.Adapter.superclass.disable.call(this);
20066     },
20067
20068     // private
20069     enable : function(){
20070         this.component.enable();
20071         Roo.menu.Adapter.superclass.enable.call(this);
20072     }
20073 });/*
20074  * Based on:
20075  * Ext JS Library 1.1.1
20076  * Copyright(c) 2006-2007, Ext JS, LLC.
20077  *
20078  * Originally Released Under LGPL - original licence link has changed is not relivant.
20079  *
20080  * Fork - LGPL
20081  * <script type="text/javascript">
20082  */
20083
20084 /**
20085  * @class Roo.menu.TextItem
20086  * @extends Roo.menu.BaseItem
20087  * Adds a static text string to a menu, usually used as either a heading or group separator.
20088  * Note: old style constructor with text is still supported.
20089  * 
20090  * @constructor
20091  * Creates a new TextItem
20092  * @param {Object} cfg Configuration
20093  */
20094 Roo.menu.TextItem = function(cfg){
20095     if (typeof(cfg) == 'string') {
20096         this.text = cfg;
20097     } else {
20098         Roo.apply(this,cfg);
20099     }
20100     
20101     Roo.menu.TextItem.superclass.constructor.call(this);
20102 };
20103
20104 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20105     /**
20106      * @cfg {Boolean} text Text to show on item.
20107      */
20108     text : '',
20109     
20110     /**
20111      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20112      */
20113     hideOnClick : false,
20114     /**
20115      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20116      */
20117     itemCls : "x-menu-text",
20118
20119     // private
20120     onRender : function(){
20121         var s = document.createElement("span");
20122         s.className = this.itemCls;
20123         s.innerHTML = this.text;
20124         this.el = s;
20125         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20126     }
20127 });/*
20128  * Based on:
20129  * Ext JS Library 1.1.1
20130  * Copyright(c) 2006-2007, Ext JS, LLC.
20131  *
20132  * Originally Released Under LGPL - original licence link has changed is not relivant.
20133  *
20134  * Fork - LGPL
20135  * <script type="text/javascript">
20136  */
20137
20138 /**
20139  * @class Roo.menu.Separator
20140  * @extends Roo.menu.BaseItem
20141  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20142  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20143  * @constructor
20144  * @param {Object} config Configuration options
20145  */
20146 Roo.menu.Separator = function(config){
20147     Roo.menu.Separator.superclass.constructor.call(this, config);
20148 };
20149
20150 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20151     /**
20152      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20153      */
20154     itemCls : "x-menu-sep",
20155     /**
20156      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20157      */
20158     hideOnClick : false,
20159
20160     // private
20161     onRender : function(li){
20162         var s = document.createElement("span");
20163         s.className = this.itemCls;
20164         s.innerHTML = "&#160;";
20165         this.el = s;
20166         li.addClass("x-menu-sep-li");
20167         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20168     }
20169 });/*
20170  * Based on:
20171  * Ext JS Library 1.1.1
20172  * Copyright(c) 2006-2007, Ext JS, LLC.
20173  *
20174  * Originally Released Under LGPL - original licence link has changed is not relivant.
20175  *
20176  * Fork - LGPL
20177  * <script type="text/javascript">
20178  */
20179 /**
20180  * @class Roo.menu.Item
20181  * @extends Roo.menu.BaseItem
20182  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20183  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20184  * activation and click handling.
20185  * @constructor
20186  * Creates a new Item
20187  * @param {Object} config Configuration options
20188  */
20189 Roo.menu.Item = function(config){
20190     Roo.menu.Item.superclass.constructor.call(this, config);
20191     if(this.menu){
20192         this.menu = Roo.menu.MenuMgr.get(this.menu);
20193     }
20194 };
20195 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20196     
20197     /**
20198      * @cfg {String} text
20199      * The text to show on the menu item.
20200      */
20201     text: '',
20202      /**
20203      * @cfg {String} HTML to render in menu
20204      * The text to show on the menu item (HTML version).
20205      */
20206     html: '',
20207     /**
20208      * @cfg {String} icon
20209      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20210      */
20211     icon: undefined,
20212     /**
20213      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20214      */
20215     itemCls : "x-menu-item",
20216     /**
20217      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20218      */
20219     canActivate : true,
20220     /**
20221      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20222      */
20223     showDelay: 200,
20224     // doc'd in BaseItem
20225     hideDelay: 200,
20226
20227     // private
20228     ctype: "Roo.menu.Item",
20229     
20230     // private
20231     onRender : function(container, position){
20232         var el = document.createElement("a");
20233         el.hideFocus = true;
20234         el.unselectable = "on";
20235         el.href = this.href || "#";
20236         if(this.hrefTarget){
20237             el.target = this.hrefTarget;
20238         }
20239         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20240         
20241         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20242         
20243         el.innerHTML = String.format(
20244                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20245                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20246         this.el = el;
20247         Roo.menu.Item.superclass.onRender.call(this, container, position);
20248     },
20249
20250     /**
20251      * Sets the text to display in this menu item
20252      * @param {String} text The text to display
20253      * @param {Boolean} isHTML true to indicate text is pure html.
20254      */
20255     setText : function(text, isHTML){
20256         if (isHTML) {
20257             this.html = text;
20258         } else {
20259             this.text = text;
20260             this.html = '';
20261         }
20262         if(this.rendered){
20263             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20264      
20265             this.el.update(String.format(
20266                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20267                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20268             this.parentMenu.autoWidth();
20269         }
20270     },
20271
20272     // private
20273     handleClick : function(e){
20274         if(!this.href){ // if no link defined, stop the event automatically
20275             e.stopEvent();
20276         }
20277         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20278     },
20279
20280     // private
20281     activate : function(autoExpand){
20282         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20283             this.focus();
20284             if(autoExpand){
20285                 this.expandMenu();
20286             }
20287         }
20288         return true;
20289     },
20290
20291     // private
20292     shouldDeactivate : function(e){
20293         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20294             if(this.menu && this.menu.isVisible()){
20295                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20296             }
20297             return true;
20298         }
20299         return false;
20300     },
20301
20302     // private
20303     deactivate : function(){
20304         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20305         this.hideMenu();
20306     },
20307
20308     // private
20309     expandMenu : function(autoActivate){
20310         if(!this.disabled && this.menu){
20311             clearTimeout(this.hideTimer);
20312             delete this.hideTimer;
20313             if(!this.menu.isVisible() && !this.showTimer){
20314                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20315             }else if (this.menu.isVisible() && autoActivate){
20316                 this.menu.tryActivate(0, 1);
20317             }
20318         }
20319     },
20320
20321     // private
20322     deferExpand : function(autoActivate){
20323         delete this.showTimer;
20324         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20325         if(autoActivate){
20326             this.menu.tryActivate(0, 1);
20327         }
20328     },
20329
20330     // private
20331     hideMenu : function(){
20332         clearTimeout(this.showTimer);
20333         delete this.showTimer;
20334         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20335             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20336         }
20337     },
20338
20339     // private
20340     deferHide : function(){
20341         delete this.hideTimer;
20342         this.menu.hide();
20343     }
20344 });/*
20345  * Based on:
20346  * Ext JS Library 1.1.1
20347  * Copyright(c) 2006-2007, Ext JS, LLC.
20348  *
20349  * Originally Released Under LGPL - original licence link has changed is not relivant.
20350  *
20351  * Fork - LGPL
20352  * <script type="text/javascript">
20353  */
20354  
20355 /**
20356  * @class Roo.menu.CheckItem
20357  * @extends Roo.menu.Item
20358  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20359  * @constructor
20360  * Creates a new CheckItem
20361  * @param {Object} config Configuration options
20362  */
20363 Roo.menu.CheckItem = function(config){
20364     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20365     this.addEvents({
20366         /**
20367          * @event beforecheckchange
20368          * Fires before the checked value is set, providing an opportunity to cancel if needed
20369          * @param {Roo.menu.CheckItem} this
20370          * @param {Boolean} checked The new checked value that will be set
20371          */
20372         "beforecheckchange" : true,
20373         /**
20374          * @event checkchange
20375          * Fires after the checked value has been set
20376          * @param {Roo.menu.CheckItem} this
20377          * @param {Boolean} checked The checked value that was set
20378          */
20379         "checkchange" : true
20380     });
20381     if(this.checkHandler){
20382         this.on('checkchange', this.checkHandler, this.scope);
20383     }
20384 };
20385 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20386     /**
20387      * @cfg {String} group
20388      * All check items with the same group name will automatically be grouped into a single-select
20389      * radio button group (defaults to '')
20390      */
20391     /**
20392      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20393      */
20394     itemCls : "x-menu-item x-menu-check-item",
20395     /**
20396      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20397      */
20398     groupClass : "x-menu-group-item",
20399
20400     /**
20401      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20402      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20403      * initialized with checked = true will be rendered as checked.
20404      */
20405     checked: false,
20406
20407     // private
20408     ctype: "Roo.menu.CheckItem",
20409
20410     // private
20411     onRender : function(c){
20412         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20413         if(this.group){
20414             this.el.addClass(this.groupClass);
20415         }
20416         Roo.menu.MenuMgr.registerCheckable(this);
20417         if(this.checked){
20418             this.checked = false;
20419             this.setChecked(true, true);
20420         }
20421     },
20422
20423     // private
20424     destroy : function(){
20425         if(this.rendered){
20426             Roo.menu.MenuMgr.unregisterCheckable(this);
20427         }
20428         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20429     },
20430
20431     /**
20432      * Set the checked state of this item
20433      * @param {Boolean} checked The new checked value
20434      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20435      */
20436     setChecked : function(state, suppressEvent){
20437         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20438             if(this.container){
20439                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20440             }
20441             this.checked = state;
20442             if(suppressEvent !== true){
20443                 this.fireEvent("checkchange", this, state);
20444             }
20445         }
20446     },
20447
20448     // private
20449     handleClick : function(e){
20450        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20451            this.setChecked(!this.checked);
20452        }
20453        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20454     }
20455 });/*
20456  * Based on:
20457  * Ext JS Library 1.1.1
20458  * Copyright(c) 2006-2007, Ext JS, LLC.
20459  *
20460  * Originally Released Under LGPL - original licence link has changed is not relivant.
20461  *
20462  * Fork - LGPL
20463  * <script type="text/javascript">
20464  */
20465  
20466 /**
20467  * @class Roo.menu.DateItem
20468  * @extends Roo.menu.Adapter
20469  * A menu item that wraps the {@link Roo.DatPicker} component.
20470  * @constructor
20471  * Creates a new DateItem
20472  * @param {Object} config Configuration options
20473  */
20474 Roo.menu.DateItem = function(config){
20475     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20476     /** The Roo.DatePicker object @type Roo.DatePicker */
20477     this.picker = this.component;
20478     this.addEvents({select: true});
20479     
20480     this.picker.on("render", function(picker){
20481         picker.getEl().swallowEvent("click");
20482         picker.container.addClass("x-menu-date-item");
20483     });
20484
20485     this.picker.on("select", this.onSelect, this);
20486 };
20487
20488 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20489     // private
20490     onSelect : function(picker, date){
20491         this.fireEvent("select", this, date, picker);
20492         Roo.menu.DateItem.superclass.handleClick.call(this);
20493     }
20494 });/*
20495  * Based on:
20496  * Ext JS Library 1.1.1
20497  * Copyright(c) 2006-2007, Ext JS, LLC.
20498  *
20499  * Originally Released Under LGPL - original licence link has changed is not relivant.
20500  *
20501  * Fork - LGPL
20502  * <script type="text/javascript">
20503  */
20504  
20505 /**
20506  * @class Roo.menu.ColorItem
20507  * @extends Roo.menu.Adapter
20508  * A menu item that wraps the {@link Roo.ColorPalette} component.
20509  * @constructor
20510  * Creates a new ColorItem
20511  * @param {Object} config Configuration options
20512  */
20513 Roo.menu.ColorItem = function(config){
20514     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20515     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20516     this.palette = this.component;
20517     this.relayEvents(this.palette, ["select"]);
20518     if(this.selectHandler){
20519         this.on('select', this.selectHandler, this.scope);
20520     }
20521 };
20522 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20523  * Based on:
20524  * Ext JS Library 1.1.1
20525  * Copyright(c) 2006-2007, Ext JS, LLC.
20526  *
20527  * Originally Released Under LGPL - original licence link has changed is not relivant.
20528  *
20529  * Fork - LGPL
20530  * <script type="text/javascript">
20531  */
20532  
20533
20534 /**
20535  * @class Roo.menu.DateMenu
20536  * @extends Roo.menu.Menu
20537  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20538  * @constructor
20539  * Creates a new DateMenu
20540  * @param {Object} config Configuration options
20541  */
20542 Roo.menu.DateMenu = function(config){
20543     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20544     this.plain = true;
20545     var di = new Roo.menu.DateItem(config);
20546     this.add(di);
20547     /**
20548      * The {@link Roo.DatePicker} instance for this DateMenu
20549      * @type DatePicker
20550      */
20551     this.picker = di.picker;
20552     /**
20553      * @event select
20554      * @param {DatePicker} picker
20555      * @param {Date} date
20556      */
20557     this.relayEvents(di, ["select"]);
20558     this.on('beforeshow', function(){
20559         if(this.picker){
20560             this.picker.hideMonthPicker(false);
20561         }
20562     }, this);
20563 };
20564 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20565     cls:'x-date-menu'
20566 });/*
20567  * Based on:
20568  * Ext JS Library 1.1.1
20569  * Copyright(c) 2006-2007, Ext JS, LLC.
20570  *
20571  * Originally Released Under LGPL - original licence link has changed is not relivant.
20572  *
20573  * Fork - LGPL
20574  * <script type="text/javascript">
20575  */
20576  
20577
20578 /**
20579  * @class Roo.menu.ColorMenu
20580  * @extends Roo.menu.Menu
20581  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20582  * @constructor
20583  * Creates a new ColorMenu
20584  * @param {Object} config Configuration options
20585  */
20586 Roo.menu.ColorMenu = function(config){
20587     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20588     this.plain = true;
20589     var ci = new Roo.menu.ColorItem(config);
20590     this.add(ci);
20591     /**
20592      * The {@link Roo.ColorPalette} instance for this ColorMenu
20593      * @type ColorPalette
20594      */
20595     this.palette = ci.palette;
20596     /**
20597      * @event select
20598      * @param {ColorPalette} palette
20599      * @param {String} color
20600      */
20601     this.relayEvents(ci, ["select"]);
20602 };
20603 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20604  * Based on:
20605  * Ext JS Library 1.1.1
20606  * Copyright(c) 2006-2007, Ext JS, LLC.
20607  *
20608  * Originally Released Under LGPL - original licence link has changed is not relivant.
20609  *
20610  * Fork - LGPL
20611  * <script type="text/javascript">
20612  */
20613  
20614 /**
20615  * @class Roo.form.Field
20616  * @extends Roo.BoxComponent
20617  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20618  * @constructor
20619  * Creates a new Field
20620  * @param {Object} config Configuration options
20621  */
20622 Roo.form.Field = function(config){
20623     Roo.form.Field.superclass.constructor.call(this, config);
20624 };
20625
20626 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20627     /**
20628      * @cfg {String} fieldLabel Label to use when rendering a form.
20629      */
20630        /**
20631      * @cfg {String} qtip Mouse over tip
20632      */
20633      
20634     /**
20635      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20636      */
20637     invalidClass : "x-form-invalid",
20638     /**
20639      * @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")
20640      */
20641     invalidText : "The value in this field is invalid",
20642     /**
20643      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20644      */
20645     focusClass : "x-form-focus",
20646     /**
20647      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20648       automatic validation (defaults to "keyup").
20649      */
20650     validationEvent : "keyup",
20651     /**
20652      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20653      */
20654     validateOnBlur : true,
20655     /**
20656      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20657      */
20658     validationDelay : 250,
20659     /**
20660      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20661      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20662      */
20663     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20664     /**
20665      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20666      */
20667     fieldClass : "x-form-field",
20668     /**
20669      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20670      *<pre>
20671 Value         Description
20672 -----------   ----------------------------------------------------------------------
20673 qtip          Display a quick tip when the user hovers over the field
20674 title         Display a default browser title attribute popup
20675 under         Add a block div beneath the field containing the error text
20676 side          Add an error icon to the right of the field with a popup on hover
20677 [element id]  Add the error text directly to the innerHTML of the specified element
20678 </pre>
20679      */
20680     msgTarget : 'qtip',
20681     /**
20682      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20683      */
20684     msgFx : 'normal',
20685
20686     /**
20687      * @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.
20688      */
20689     readOnly : false,
20690
20691     /**
20692      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20693      */
20694     disabled : false,
20695
20696     /**
20697      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20698      */
20699     inputType : undefined,
20700     
20701     /**
20702      * @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).
20703          */
20704         tabIndex : undefined,
20705         
20706     // private
20707     isFormField : true,
20708
20709     // private
20710     hasFocus : false,
20711     /**
20712      * @property {Roo.Element} fieldEl
20713      * Element Containing the rendered Field (with label etc.)
20714      */
20715     /**
20716      * @cfg {Mixed} value A value to initialize this field with.
20717      */
20718     value : undefined,
20719
20720     /**
20721      * @cfg {String} name The field's HTML name attribute.
20722      */
20723     /**
20724      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20725      */
20726     // private
20727     loadedValue : false,
20728      
20729      
20730         // private ??
20731         initComponent : function(){
20732         Roo.form.Field.superclass.initComponent.call(this);
20733         this.addEvents({
20734             /**
20735              * @event focus
20736              * Fires when this field receives input focus.
20737              * @param {Roo.form.Field} this
20738              */
20739             focus : true,
20740             /**
20741              * @event blur
20742              * Fires when this field loses input focus.
20743              * @param {Roo.form.Field} this
20744              */
20745             blur : true,
20746             /**
20747              * @event specialkey
20748              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20749              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20750              * @param {Roo.form.Field} this
20751              * @param {Roo.EventObject} e The event object
20752              */
20753             specialkey : true,
20754             /**
20755              * @event change
20756              * Fires just before the field blurs if the field value has changed.
20757              * @param {Roo.form.Field} this
20758              * @param {Mixed} newValue The new value
20759              * @param {Mixed} oldValue The original value
20760              */
20761             change : true,
20762             /**
20763              * @event invalid
20764              * Fires after the field has been marked as invalid.
20765              * @param {Roo.form.Field} this
20766              * @param {String} msg The validation message
20767              */
20768             invalid : true,
20769             /**
20770              * @event valid
20771              * Fires after the field has been validated with no errors.
20772              * @param {Roo.form.Field} this
20773              */
20774             valid : true,
20775              /**
20776              * @event keyup
20777              * Fires after the key up
20778              * @param {Roo.form.Field} this
20779              * @param {Roo.EventObject}  e The event Object
20780              */
20781             keyup : true
20782         });
20783     },
20784
20785     /**
20786      * Returns the name attribute of the field if available
20787      * @return {String} name The field name
20788      */
20789     getName: function(){
20790          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20791     },
20792
20793     // private
20794     onRender : function(ct, position){
20795         Roo.form.Field.superclass.onRender.call(this, ct, position);
20796         if(!this.el){
20797             var cfg = this.getAutoCreate();
20798             if(!cfg.name){
20799                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20800             }
20801             if (!cfg.name.length) {
20802                 delete cfg.name;
20803             }
20804             if(this.inputType){
20805                 cfg.type = this.inputType;
20806             }
20807             this.el = ct.createChild(cfg, position);
20808         }
20809         var type = this.el.dom.type;
20810         if(type){
20811             if(type == 'password'){
20812                 type = 'text';
20813             }
20814             this.el.addClass('x-form-'+type);
20815         }
20816         if(this.readOnly){
20817             this.el.dom.readOnly = true;
20818         }
20819         if(this.tabIndex !== undefined){
20820             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20821         }
20822
20823         this.el.addClass([this.fieldClass, this.cls]);
20824         this.initValue();
20825     },
20826
20827     /**
20828      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20829      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20830      * @return {Roo.form.Field} this
20831      */
20832     applyTo : function(target){
20833         this.allowDomMove = false;
20834         this.el = Roo.get(target);
20835         this.render(this.el.dom.parentNode);
20836         return this;
20837     },
20838
20839     // private
20840     initValue : function(){
20841         if(this.value !== undefined){
20842             this.setValue(this.value);
20843         }else if(this.el.dom.value.length > 0){
20844             this.setValue(this.el.dom.value);
20845         }
20846     },
20847
20848     /**
20849      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20850      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
20851      */
20852     isDirty : function() {
20853         if(this.disabled) {
20854             return false;
20855         }
20856         return String(this.getValue()) !== String(this.originalValue);
20857     },
20858
20859     /**
20860      * stores the current value in loadedValue
20861      */
20862     resetHasChanged : function()
20863     {
20864         this.loadedValue = String(this.getValue());
20865     },
20866     /**
20867      * checks the current value against the 'loaded' value.
20868      * Note - will return false if 'resetHasChanged' has not been called first.
20869      */
20870     hasChanged : function()
20871     {
20872         if(this.disabled || this.readOnly) {
20873             return false;
20874         }
20875         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
20876     },
20877     
20878     
20879     
20880     // private
20881     afterRender : function(){
20882         Roo.form.Field.superclass.afterRender.call(this);
20883         this.initEvents();
20884     },
20885
20886     // private
20887     fireKey : function(e){
20888         //Roo.log('field ' + e.getKey());
20889         if(e.isNavKeyPress()){
20890             this.fireEvent("specialkey", this, e);
20891         }
20892     },
20893
20894     /**
20895      * Resets the current field value to the originally loaded value and clears any validation messages
20896      */
20897     reset : function(){
20898         this.setValue(this.resetValue);
20899         this.clearInvalid();
20900     },
20901
20902     // private
20903     initEvents : function(){
20904         // safari killled keypress - so keydown is now used..
20905         this.el.on("keydown" , this.fireKey,  this);
20906         this.el.on("focus", this.onFocus,  this);
20907         this.el.on("blur", this.onBlur,  this);
20908         this.el.relayEvent('keyup', this);
20909
20910         // reference to original value for reset
20911         this.originalValue = this.getValue();
20912         this.resetValue =  this.getValue();
20913     },
20914
20915     // private
20916     onFocus : function(){
20917         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20918             this.el.addClass(this.focusClass);
20919         }
20920         if(!this.hasFocus){
20921             this.hasFocus = true;
20922             this.startValue = this.getValue();
20923             this.fireEvent("focus", this);
20924         }
20925     },
20926
20927     beforeBlur : Roo.emptyFn,
20928
20929     // private
20930     onBlur : function(){
20931         this.beforeBlur();
20932         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20933             this.el.removeClass(this.focusClass);
20934         }
20935         this.hasFocus = false;
20936         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20937             this.validate();
20938         }
20939         var v = this.getValue();
20940         if(String(v) !== String(this.startValue)){
20941             this.fireEvent('change', this, v, this.startValue);
20942         }
20943         this.fireEvent("blur", this);
20944     },
20945
20946     /**
20947      * Returns whether or not the field value is currently valid
20948      * @param {Boolean} preventMark True to disable marking the field invalid
20949      * @return {Boolean} True if the value is valid, else false
20950      */
20951     isValid : function(preventMark){
20952         if(this.disabled){
20953             return true;
20954         }
20955         var restore = this.preventMark;
20956         this.preventMark = preventMark === true;
20957         var v = this.validateValue(this.processValue(this.getRawValue()));
20958         this.preventMark = restore;
20959         return v;
20960     },
20961
20962     /**
20963      * Validates the field value
20964      * @return {Boolean} True if the value is valid, else false
20965      */
20966     validate : function(){
20967         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20968             this.clearInvalid();
20969             return true;
20970         }
20971         return false;
20972     },
20973
20974     processValue : function(value){
20975         return value;
20976     },
20977
20978     // private
20979     // Subclasses should provide the validation implementation by overriding this
20980     validateValue : function(value){
20981         return true;
20982     },
20983
20984     /**
20985      * Mark this field as invalid
20986      * @param {String} msg The validation message
20987      */
20988     markInvalid : function(msg){
20989         if(!this.rendered || this.preventMark){ // not rendered
20990             return;
20991         }
20992         
20993         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20994         
20995         obj.el.addClass(this.invalidClass);
20996         msg = msg || this.invalidText;
20997         switch(this.msgTarget){
20998             case 'qtip':
20999                 obj.el.dom.qtip = msg;
21000                 obj.el.dom.qclass = 'x-form-invalid-tip';
21001                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21002                     Roo.QuickTips.enable();
21003                 }
21004                 break;
21005             case 'title':
21006                 this.el.dom.title = msg;
21007                 break;
21008             case 'under':
21009                 if(!this.errorEl){
21010                     var elp = this.el.findParent('.x-form-element', 5, true);
21011                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21012                     this.errorEl.setWidth(elp.getWidth(true)-20);
21013                 }
21014                 this.errorEl.update(msg);
21015                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21016                 break;
21017             case 'side':
21018                 if(!this.errorIcon){
21019                     var elp = this.el.findParent('.x-form-element', 5, true);
21020                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21021                 }
21022                 this.alignErrorIcon();
21023                 this.errorIcon.dom.qtip = msg;
21024                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21025                 this.errorIcon.show();
21026                 this.on('resize', this.alignErrorIcon, this);
21027                 break;
21028             default:
21029                 var t = Roo.getDom(this.msgTarget);
21030                 t.innerHTML = msg;
21031                 t.style.display = this.msgDisplay;
21032                 break;
21033         }
21034         this.fireEvent('invalid', this, msg);
21035     },
21036
21037     // private
21038     alignErrorIcon : function(){
21039         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21040     },
21041
21042     /**
21043      * Clear any invalid styles/messages for this field
21044      */
21045     clearInvalid : function(){
21046         if(!this.rendered || this.preventMark){ // not rendered
21047             return;
21048         }
21049         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21050         
21051         obj.el.removeClass(this.invalidClass);
21052         switch(this.msgTarget){
21053             case 'qtip':
21054                 obj.el.dom.qtip = '';
21055                 break;
21056             case 'title':
21057                 this.el.dom.title = '';
21058                 break;
21059             case 'under':
21060                 if(this.errorEl){
21061                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21062                 }
21063                 break;
21064             case 'side':
21065                 if(this.errorIcon){
21066                     this.errorIcon.dom.qtip = '';
21067                     this.errorIcon.hide();
21068                     this.un('resize', this.alignErrorIcon, this);
21069                 }
21070                 break;
21071             default:
21072                 var t = Roo.getDom(this.msgTarget);
21073                 t.innerHTML = '';
21074                 t.style.display = 'none';
21075                 break;
21076         }
21077         this.fireEvent('valid', this);
21078     },
21079
21080     /**
21081      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21082      * @return {Mixed} value The field value
21083      */
21084     getRawValue : function(){
21085         var v = this.el.getValue();
21086         
21087         return v;
21088     },
21089
21090     /**
21091      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21092      * @return {Mixed} value The field value
21093      */
21094     getValue : function(){
21095         var v = this.el.getValue();
21096          
21097         return v;
21098     },
21099
21100     /**
21101      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21102      * @param {Mixed} value The value to set
21103      */
21104     setRawValue : function(v){
21105         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21106     },
21107
21108     /**
21109      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21110      * @param {Mixed} value The value to set
21111      */
21112     setValue : function(v){
21113         this.value = v;
21114         if(this.rendered){
21115             this.el.dom.value = (v === null || v === undefined ? '' : v);
21116              this.validate();
21117         }
21118     },
21119
21120     adjustSize : function(w, h){
21121         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21122         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21123         return s;
21124     },
21125
21126     adjustWidth : function(tag, w){
21127         tag = tag.toLowerCase();
21128         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21129             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21130                 if(tag == 'input'){
21131                     return w + 2;
21132                 }
21133                 if(tag == 'textarea'){
21134                     return w-2;
21135                 }
21136             }else if(Roo.isOpera){
21137                 if(tag == 'input'){
21138                     return w + 2;
21139                 }
21140                 if(tag == 'textarea'){
21141                     return w-2;
21142                 }
21143             }
21144         }
21145         return w;
21146     }
21147 });
21148
21149
21150 // anything other than normal should be considered experimental
21151 Roo.form.Field.msgFx = {
21152     normal : {
21153         show: function(msgEl, f){
21154             msgEl.setDisplayed('block');
21155         },
21156
21157         hide : function(msgEl, f){
21158             msgEl.setDisplayed(false).update('');
21159         }
21160     },
21161
21162     slide : {
21163         show: function(msgEl, f){
21164             msgEl.slideIn('t', {stopFx:true});
21165         },
21166
21167         hide : function(msgEl, f){
21168             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21169         }
21170     },
21171
21172     slideRight : {
21173         show: function(msgEl, f){
21174             msgEl.fixDisplay();
21175             msgEl.alignTo(f.el, 'tl-tr');
21176             msgEl.slideIn('l', {stopFx:true});
21177         },
21178
21179         hide : function(msgEl, f){
21180             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21181         }
21182     }
21183 };/*
21184  * Based on:
21185  * Ext JS Library 1.1.1
21186  * Copyright(c) 2006-2007, Ext JS, LLC.
21187  *
21188  * Originally Released Under LGPL - original licence link has changed is not relivant.
21189  *
21190  * Fork - LGPL
21191  * <script type="text/javascript">
21192  */
21193  
21194
21195 /**
21196  * @class Roo.form.TextField
21197  * @extends Roo.form.Field
21198  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21199  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21200  * @constructor
21201  * Creates a new TextField
21202  * @param {Object} config Configuration options
21203  */
21204 Roo.form.TextField = function(config){
21205     Roo.form.TextField.superclass.constructor.call(this, config);
21206     this.addEvents({
21207         /**
21208          * @event autosize
21209          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21210          * according to the default logic, but this event provides a hook for the developer to apply additional
21211          * logic at runtime to resize the field if needed.
21212              * @param {Roo.form.Field} this This text field
21213              * @param {Number} width The new field width
21214              */
21215         autosize : true
21216     });
21217 };
21218
21219 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21220     /**
21221      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21222      */
21223     grow : false,
21224     /**
21225      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21226      */
21227     growMin : 30,
21228     /**
21229      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21230      */
21231     growMax : 800,
21232     /**
21233      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21234      */
21235     vtype : null,
21236     /**
21237      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21238      */
21239     maskRe : null,
21240     /**
21241      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21242      */
21243     disableKeyFilter : false,
21244     /**
21245      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21246      */
21247     allowBlank : true,
21248     /**
21249      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21250      */
21251     minLength : 0,
21252     /**
21253      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21254      */
21255     maxLength : Number.MAX_VALUE,
21256     /**
21257      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21258      */
21259     minLengthText : "The minimum length for this field is {0}",
21260     /**
21261      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21262      */
21263     maxLengthText : "The maximum length for this field is {0}",
21264     /**
21265      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21266      */
21267     selectOnFocus : false,
21268     /**
21269      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21270      */
21271     blankText : "This field is required",
21272     /**
21273      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21274      * If available, this function will be called only after the basic validators all return true, and will be passed the
21275      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21276      */
21277     validator : null,
21278     /**
21279      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21280      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21281      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21282      */
21283     regex : null,
21284     /**
21285      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21286      */
21287     regexText : "",
21288     /**
21289      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21290      */
21291     emptyText : null,
21292    
21293
21294     // private
21295     initEvents : function()
21296     {
21297         if (this.emptyText) {
21298             this.el.attr('placeholder', this.emptyText);
21299         }
21300         
21301         Roo.form.TextField.superclass.initEvents.call(this);
21302         if(this.validationEvent == 'keyup'){
21303             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21304             this.el.on('keyup', this.filterValidation, this);
21305         }
21306         else if(this.validationEvent !== false){
21307             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21308         }
21309         
21310         if(this.selectOnFocus){
21311             this.on("focus", this.preFocus, this);
21312             
21313         }
21314         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21315             this.el.on("keypress", this.filterKeys, this);
21316         }
21317         if(this.grow){
21318             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21319             this.el.on("click", this.autoSize,  this);
21320         }
21321         if(this.el.is('input[type=password]') && Roo.isSafari){
21322             this.el.on('keydown', this.SafariOnKeyDown, this);
21323         }
21324     },
21325
21326     processValue : function(value){
21327         if(this.stripCharsRe){
21328             var newValue = value.replace(this.stripCharsRe, '');
21329             if(newValue !== value){
21330                 this.setRawValue(newValue);
21331                 return newValue;
21332             }
21333         }
21334         return value;
21335     },
21336
21337     filterValidation : function(e){
21338         if(!e.isNavKeyPress()){
21339             this.validationTask.delay(this.validationDelay);
21340         }
21341     },
21342
21343     // private
21344     onKeyUp : function(e){
21345         if(!e.isNavKeyPress()){
21346             this.autoSize();
21347         }
21348     },
21349
21350     /**
21351      * Resets the current field value to the originally-loaded value and clears any validation messages.
21352      *  
21353      */
21354     reset : function(){
21355         Roo.form.TextField.superclass.reset.call(this);
21356        
21357     },
21358
21359     
21360     // private
21361     preFocus : function(){
21362         
21363         if(this.selectOnFocus){
21364             this.el.dom.select();
21365         }
21366     },
21367
21368     
21369     // private
21370     filterKeys : function(e){
21371         var k = e.getKey();
21372         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21373             return;
21374         }
21375         var c = e.getCharCode(), cc = String.fromCharCode(c);
21376         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21377             return;
21378         }
21379         if(!this.maskRe.test(cc)){
21380             e.stopEvent();
21381         }
21382     },
21383
21384     setValue : function(v){
21385         
21386         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21387         
21388         this.autoSize();
21389     },
21390
21391     /**
21392      * Validates a value according to the field's validation rules and marks the field as invalid
21393      * if the validation fails
21394      * @param {Mixed} value The value to validate
21395      * @return {Boolean} True if the value is valid, else false
21396      */
21397     validateValue : function(value){
21398         if(value.length < 1)  { // if it's blank
21399              if(this.allowBlank){
21400                 this.clearInvalid();
21401                 return true;
21402              }else{
21403                 this.markInvalid(this.blankText);
21404                 return false;
21405              }
21406         }
21407         if(value.length < this.minLength){
21408             this.markInvalid(String.format(this.minLengthText, this.minLength));
21409             return false;
21410         }
21411         if(value.length > this.maxLength){
21412             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21413             return false;
21414         }
21415         if(this.vtype){
21416             var vt = Roo.form.VTypes;
21417             if(!vt[this.vtype](value, this)){
21418                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21419                 return false;
21420             }
21421         }
21422         if(typeof this.validator == "function"){
21423             var msg = this.validator(value);
21424             if(msg !== true){
21425                 this.markInvalid(msg);
21426                 return false;
21427             }
21428         }
21429         if(this.regex && !this.regex.test(value)){
21430             this.markInvalid(this.regexText);
21431             return false;
21432         }
21433         return true;
21434     },
21435
21436     /**
21437      * Selects text in this field
21438      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21439      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21440      */
21441     selectText : function(start, end){
21442         var v = this.getRawValue();
21443         if(v.length > 0){
21444             start = start === undefined ? 0 : start;
21445             end = end === undefined ? v.length : end;
21446             var d = this.el.dom;
21447             if(d.setSelectionRange){
21448                 d.setSelectionRange(start, end);
21449             }else if(d.createTextRange){
21450                 var range = d.createTextRange();
21451                 range.moveStart("character", start);
21452                 range.moveEnd("character", v.length-end);
21453                 range.select();
21454             }
21455         }
21456     },
21457
21458     /**
21459      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21460      * This only takes effect if grow = true, and fires the autosize event.
21461      */
21462     autoSize : function(){
21463         if(!this.grow || !this.rendered){
21464             return;
21465         }
21466         if(!this.metrics){
21467             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21468         }
21469         var el = this.el;
21470         var v = el.dom.value;
21471         var d = document.createElement('div');
21472         d.appendChild(document.createTextNode(v));
21473         v = d.innerHTML;
21474         d = null;
21475         v += "&#160;";
21476         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21477         this.el.setWidth(w);
21478         this.fireEvent("autosize", this, w);
21479     },
21480     
21481     // private
21482     SafariOnKeyDown : function(event)
21483     {
21484         // this is a workaround for a password hang bug on chrome/ webkit.
21485         
21486         var isSelectAll = false;
21487         
21488         if(this.el.dom.selectionEnd > 0){
21489             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21490         }
21491         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21492             event.preventDefault();
21493             this.setValue('');
21494             return;
21495         }
21496         
21497         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21498             
21499             event.preventDefault();
21500             // this is very hacky as keydown always get's upper case.
21501             
21502             var cc = String.fromCharCode(event.getCharCode());
21503             
21504             
21505             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21506             
21507         }
21508         
21509         
21510     }
21511 });/*
21512  * Based on:
21513  * Ext JS Library 1.1.1
21514  * Copyright(c) 2006-2007, Ext JS, LLC.
21515  *
21516  * Originally Released Under LGPL - original licence link has changed is not relivant.
21517  *
21518  * Fork - LGPL
21519  * <script type="text/javascript">
21520  */
21521  
21522 /**
21523  * @class Roo.form.Hidden
21524  * @extends Roo.form.TextField
21525  * Simple Hidden element used on forms 
21526  * 
21527  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21528  * 
21529  * @constructor
21530  * Creates a new Hidden form element.
21531  * @param {Object} config Configuration options
21532  */
21533
21534
21535
21536 // easy hidden field...
21537 Roo.form.Hidden = function(config){
21538     Roo.form.Hidden.superclass.constructor.call(this, config);
21539 };
21540   
21541 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21542     fieldLabel:      '',
21543     inputType:      'hidden',
21544     width:          50,
21545     allowBlank:     true,
21546     labelSeparator: '',
21547     hidden:         true,
21548     itemCls :       'x-form-item-display-none'
21549
21550
21551 });
21552
21553
21554 /*
21555  * Based on:
21556  * Ext JS Library 1.1.1
21557  * Copyright(c) 2006-2007, Ext JS, LLC.
21558  *
21559  * Originally Released Under LGPL - original licence link has changed is not relivant.
21560  *
21561  * Fork - LGPL
21562  * <script type="text/javascript">
21563  */
21564  
21565 /**
21566  * @class Roo.form.TriggerField
21567  * @extends Roo.form.TextField
21568  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21569  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21570  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21571  * for which you can provide a custom implementation.  For example:
21572  * <pre><code>
21573 var trigger = new Roo.form.TriggerField();
21574 trigger.onTriggerClick = myTriggerFn;
21575 trigger.applyTo('my-field');
21576 </code></pre>
21577  *
21578  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21579  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21580  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21581  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21582  * @constructor
21583  * Create a new TriggerField.
21584  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21585  * to the base TextField)
21586  */
21587 Roo.form.TriggerField = function(config){
21588     this.mimicing = false;
21589     Roo.form.TriggerField.superclass.constructor.call(this, config);
21590 };
21591
21592 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21593     /**
21594      * @cfg {String} triggerClass A CSS class to apply to the trigger
21595      */
21596     /**
21597      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21598      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21599      */
21600     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21601     /**
21602      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21603      */
21604     hideTrigger:false,
21605
21606     /** @cfg {Boolean} grow @hide */
21607     /** @cfg {Number} growMin @hide */
21608     /** @cfg {Number} growMax @hide */
21609
21610     /**
21611      * @hide 
21612      * @method
21613      */
21614     autoSize: Roo.emptyFn,
21615     // private
21616     monitorTab : true,
21617     // private
21618     deferHeight : true,
21619
21620     
21621     actionMode : 'wrap',
21622     // private
21623     onResize : function(w, h){
21624         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21625         if(typeof w == 'number'){
21626             var x = w - this.trigger.getWidth();
21627             this.el.setWidth(this.adjustWidth('input', x));
21628             this.trigger.setStyle('left', x+'px');
21629         }
21630     },
21631
21632     // private
21633     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21634
21635     // private
21636     getResizeEl : function(){
21637         return this.wrap;
21638     },
21639
21640     // private
21641     getPositionEl : function(){
21642         return this.wrap;
21643     },
21644
21645     // private
21646     alignErrorIcon : function(){
21647         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21648     },
21649
21650     // private
21651     onRender : function(ct, position){
21652         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21653         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21654         this.trigger = this.wrap.createChild(this.triggerConfig ||
21655                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21656         if(this.hideTrigger){
21657             this.trigger.setDisplayed(false);
21658         }
21659         this.initTrigger();
21660         if(!this.width){
21661             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21662         }
21663     },
21664
21665     // private
21666     initTrigger : function(){
21667         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21668         this.trigger.addClassOnOver('x-form-trigger-over');
21669         this.trigger.addClassOnClick('x-form-trigger-click');
21670     },
21671
21672     // private
21673     onDestroy : function(){
21674         if(this.trigger){
21675             this.trigger.removeAllListeners();
21676             this.trigger.remove();
21677         }
21678         if(this.wrap){
21679             this.wrap.remove();
21680         }
21681         Roo.form.TriggerField.superclass.onDestroy.call(this);
21682     },
21683
21684     // private
21685     onFocus : function(){
21686         Roo.form.TriggerField.superclass.onFocus.call(this);
21687         if(!this.mimicing){
21688             this.wrap.addClass('x-trigger-wrap-focus');
21689             this.mimicing = true;
21690             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21691             if(this.monitorTab){
21692                 this.el.on("keydown", this.checkTab, this);
21693             }
21694         }
21695     },
21696
21697     // private
21698     checkTab : function(e){
21699         if(e.getKey() == e.TAB){
21700             this.triggerBlur();
21701         }
21702     },
21703
21704     // private
21705     onBlur : function(){
21706         // do nothing
21707     },
21708
21709     // private
21710     mimicBlur : function(e, t){
21711         if(!this.wrap.contains(t) && this.validateBlur()){
21712             this.triggerBlur();
21713         }
21714     },
21715
21716     // private
21717     triggerBlur : function(){
21718         this.mimicing = false;
21719         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21720         if(this.monitorTab){
21721             this.el.un("keydown", this.checkTab, this);
21722         }
21723         this.wrap.removeClass('x-trigger-wrap-focus');
21724         Roo.form.TriggerField.superclass.onBlur.call(this);
21725     },
21726
21727     // private
21728     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21729     validateBlur : function(e, t){
21730         return true;
21731     },
21732
21733     // private
21734     onDisable : function(){
21735         Roo.form.TriggerField.superclass.onDisable.call(this);
21736         if(this.wrap){
21737             this.wrap.addClass('x-item-disabled');
21738         }
21739     },
21740
21741     // private
21742     onEnable : function(){
21743         Roo.form.TriggerField.superclass.onEnable.call(this);
21744         if(this.wrap){
21745             this.wrap.removeClass('x-item-disabled');
21746         }
21747     },
21748
21749     // private
21750     onShow : function(){
21751         var ae = this.getActionEl();
21752         
21753         if(ae){
21754             ae.dom.style.display = '';
21755             ae.dom.style.visibility = 'visible';
21756         }
21757     },
21758
21759     // private
21760     
21761     onHide : function(){
21762         var ae = this.getActionEl();
21763         ae.dom.style.display = 'none';
21764     },
21765
21766     /**
21767      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21768      * by an implementing function.
21769      * @method
21770      * @param {EventObject} e
21771      */
21772     onTriggerClick : Roo.emptyFn
21773 });
21774
21775 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21776 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21777 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21778 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21779     initComponent : function(){
21780         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21781
21782         this.triggerConfig = {
21783             tag:'span', cls:'x-form-twin-triggers', cn:[
21784             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21785             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21786         ]};
21787     },
21788
21789     getTrigger : function(index){
21790         return this.triggers[index];
21791     },
21792
21793     initTrigger : function(){
21794         var ts = this.trigger.select('.x-form-trigger', true);
21795         this.wrap.setStyle('overflow', 'hidden');
21796         var triggerField = this;
21797         ts.each(function(t, all, index){
21798             t.hide = function(){
21799                 var w = triggerField.wrap.getWidth();
21800                 this.dom.style.display = 'none';
21801                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21802             };
21803             t.show = function(){
21804                 var w = triggerField.wrap.getWidth();
21805                 this.dom.style.display = '';
21806                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21807             };
21808             var triggerIndex = 'Trigger'+(index+1);
21809
21810             if(this['hide'+triggerIndex]){
21811                 t.dom.style.display = 'none';
21812             }
21813             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21814             t.addClassOnOver('x-form-trigger-over');
21815             t.addClassOnClick('x-form-trigger-click');
21816         }, this);
21817         this.triggers = ts.elements;
21818     },
21819
21820     onTrigger1Click : Roo.emptyFn,
21821     onTrigger2Click : Roo.emptyFn
21822 });/*
21823  * Based on:
21824  * Ext JS Library 1.1.1
21825  * Copyright(c) 2006-2007, Ext JS, LLC.
21826  *
21827  * Originally Released Under LGPL - original licence link has changed is not relivant.
21828  *
21829  * Fork - LGPL
21830  * <script type="text/javascript">
21831  */
21832  
21833 /**
21834  * @class Roo.form.TextArea
21835  * @extends Roo.form.TextField
21836  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21837  * support for auto-sizing.
21838  * @constructor
21839  * Creates a new TextArea
21840  * @param {Object} config Configuration options
21841  */
21842 Roo.form.TextArea = function(config){
21843     Roo.form.TextArea.superclass.constructor.call(this, config);
21844     // these are provided exchanges for backwards compat
21845     // minHeight/maxHeight were replaced by growMin/growMax to be
21846     // compatible with TextField growing config values
21847     if(this.minHeight !== undefined){
21848         this.growMin = this.minHeight;
21849     }
21850     if(this.maxHeight !== undefined){
21851         this.growMax = this.maxHeight;
21852     }
21853 };
21854
21855 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21856     /**
21857      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21858      */
21859     growMin : 60,
21860     /**
21861      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21862      */
21863     growMax: 1000,
21864     /**
21865      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21866      * in the field (equivalent to setting overflow: hidden, defaults to false)
21867      */
21868     preventScrollbars: false,
21869     /**
21870      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21871      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21872      */
21873
21874     // private
21875     onRender : function(ct, position){
21876         if(!this.el){
21877             this.defaultAutoCreate = {
21878                 tag: "textarea",
21879                 style:"width:300px;height:60px;",
21880                 autocomplete: "new-password"
21881             };
21882         }
21883         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21884         if(this.grow){
21885             this.textSizeEl = Roo.DomHelper.append(document.body, {
21886                 tag: "pre", cls: "x-form-grow-sizer"
21887             });
21888             if(this.preventScrollbars){
21889                 this.el.setStyle("overflow", "hidden");
21890             }
21891             this.el.setHeight(this.growMin);
21892         }
21893     },
21894
21895     onDestroy : function(){
21896         if(this.textSizeEl){
21897             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21898         }
21899         Roo.form.TextArea.superclass.onDestroy.call(this);
21900     },
21901
21902     // private
21903     onKeyUp : function(e){
21904         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21905             this.autoSize();
21906         }
21907     },
21908
21909     /**
21910      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21911      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21912      */
21913     autoSize : function(){
21914         if(!this.grow || !this.textSizeEl){
21915             return;
21916         }
21917         var el = this.el;
21918         var v = el.dom.value;
21919         var ts = this.textSizeEl;
21920
21921         ts.innerHTML = '';
21922         ts.appendChild(document.createTextNode(v));
21923         v = ts.innerHTML;
21924
21925         Roo.fly(ts).setWidth(this.el.getWidth());
21926         if(v.length < 1){
21927             v = "&#160;&#160;";
21928         }else{
21929             if(Roo.isIE){
21930                 v = v.replace(/\n/g, '<p>&#160;</p>');
21931             }
21932             v += "&#160;\n&#160;";
21933         }
21934         ts.innerHTML = v;
21935         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21936         if(h != this.lastHeight){
21937             this.lastHeight = h;
21938             this.el.setHeight(h);
21939             this.fireEvent("autosize", this, h);
21940         }
21941     }
21942 });/*
21943  * Based on:
21944  * Ext JS Library 1.1.1
21945  * Copyright(c) 2006-2007, Ext JS, LLC.
21946  *
21947  * Originally Released Under LGPL - original licence link has changed is not relivant.
21948  *
21949  * Fork - LGPL
21950  * <script type="text/javascript">
21951  */
21952  
21953
21954 /**
21955  * @class Roo.form.NumberField
21956  * @extends Roo.form.TextField
21957  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21958  * @constructor
21959  * Creates a new NumberField
21960  * @param {Object} config Configuration options
21961  */
21962 Roo.form.NumberField = function(config){
21963     Roo.form.NumberField.superclass.constructor.call(this, config);
21964 };
21965
21966 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21967     /**
21968      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21969      */
21970     fieldClass: "x-form-field x-form-num-field",
21971     /**
21972      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21973      */
21974     allowDecimals : true,
21975     /**
21976      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21977      */
21978     decimalSeparator : ".",
21979     /**
21980      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21981      */
21982     decimalPrecision : 2,
21983     /**
21984      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21985      */
21986     allowNegative : true,
21987     /**
21988      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21989      */
21990     minValue : Number.NEGATIVE_INFINITY,
21991     /**
21992      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21993      */
21994     maxValue : Number.MAX_VALUE,
21995     /**
21996      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21997      */
21998     minText : "The minimum value for this field is {0}",
21999     /**
22000      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22001      */
22002     maxText : "The maximum value for this field is {0}",
22003     /**
22004      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22005      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22006      */
22007     nanText : "{0} is not a valid number",
22008
22009     // private
22010     initEvents : function(){
22011         Roo.form.NumberField.superclass.initEvents.call(this);
22012         var allowed = "0123456789";
22013         if(this.allowDecimals){
22014             allowed += this.decimalSeparator;
22015         }
22016         if(this.allowNegative){
22017             allowed += "-";
22018         }
22019         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22020         var keyPress = function(e){
22021             var k = e.getKey();
22022             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22023                 return;
22024             }
22025             var c = e.getCharCode();
22026             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22027                 e.stopEvent();
22028             }
22029         };
22030         this.el.on("keypress", keyPress, this);
22031     },
22032
22033     // private
22034     validateValue : function(value){
22035         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22036             return false;
22037         }
22038         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22039              return true;
22040         }
22041         var num = this.parseValue(value);
22042         if(isNaN(num)){
22043             this.markInvalid(String.format(this.nanText, value));
22044             return false;
22045         }
22046         if(num < this.minValue){
22047             this.markInvalid(String.format(this.minText, this.minValue));
22048             return false;
22049         }
22050         if(num > this.maxValue){
22051             this.markInvalid(String.format(this.maxText, this.maxValue));
22052             return false;
22053         }
22054         return true;
22055     },
22056
22057     getValue : function(){
22058         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22059     },
22060
22061     // private
22062     parseValue : function(value){
22063         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22064         return isNaN(value) ? '' : value;
22065     },
22066
22067     // private
22068     fixPrecision : function(value){
22069         var nan = isNaN(value);
22070         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22071             return nan ? '' : value;
22072         }
22073         return parseFloat(value).toFixed(this.decimalPrecision);
22074     },
22075
22076     setValue : function(v){
22077         v = this.fixPrecision(v);
22078         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22079     },
22080
22081     // private
22082     decimalPrecisionFcn : function(v){
22083         return Math.floor(v);
22084     },
22085
22086     beforeBlur : function(){
22087         var v = this.parseValue(this.getRawValue());
22088         if(v){
22089             this.setValue(v);
22090         }
22091     }
22092 });/*
22093  * Based on:
22094  * Ext JS Library 1.1.1
22095  * Copyright(c) 2006-2007, Ext JS, LLC.
22096  *
22097  * Originally Released Under LGPL - original licence link has changed is not relivant.
22098  *
22099  * Fork - LGPL
22100  * <script type="text/javascript">
22101  */
22102  
22103 /**
22104  * @class Roo.form.DateField
22105  * @extends Roo.form.TriggerField
22106  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22107 * @constructor
22108 * Create a new DateField
22109 * @param {Object} config
22110  */
22111 Roo.form.DateField = function(config){
22112     Roo.form.DateField.superclass.constructor.call(this, config);
22113     
22114       this.addEvents({
22115          
22116         /**
22117          * @event select
22118          * Fires when a date is selected
22119              * @param {Roo.form.DateField} combo This combo box
22120              * @param {Date} date The date selected
22121              */
22122         'select' : true
22123          
22124     });
22125     
22126     
22127     if(typeof this.minValue == "string") {
22128         this.minValue = this.parseDate(this.minValue);
22129     }
22130     if(typeof this.maxValue == "string") {
22131         this.maxValue = this.parseDate(this.maxValue);
22132     }
22133     this.ddMatch = null;
22134     if(this.disabledDates){
22135         var dd = this.disabledDates;
22136         var re = "(?:";
22137         for(var i = 0; i < dd.length; i++){
22138             re += dd[i];
22139             if(i != dd.length-1) {
22140                 re += "|";
22141             }
22142         }
22143         this.ddMatch = new RegExp(re + ")");
22144     }
22145 };
22146
22147 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22148     /**
22149      * @cfg {String} format
22150      * The default date format string which can be overriden for localization support.  The format must be
22151      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22152      */
22153     format : "m/d/y",
22154     /**
22155      * @cfg {String} altFormats
22156      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22157      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22158      */
22159     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22160     /**
22161      * @cfg {Array} disabledDays
22162      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22163      */
22164     disabledDays : null,
22165     /**
22166      * @cfg {String} disabledDaysText
22167      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22168      */
22169     disabledDaysText : "Disabled",
22170     /**
22171      * @cfg {Array} disabledDates
22172      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22173      * expression so they are very powerful. Some examples:
22174      * <ul>
22175      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22176      * <li>["03/08", "09/16"] would disable those days for every year</li>
22177      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22178      * <li>["03/../2006"] would disable every day in March 2006</li>
22179      * <li>["^03"] would disable every day in every March</li>
22180      * </ul>
22181      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22182      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22183      */
22184     disabledDates : null,
22185     /**
22186      * @cfg {String} disabledDatesText
22187      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22188      */
22189     disabledDatesText : "Disabled",
22190     /**
22191      * @cfg {Date/String} minValue
22192      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22193      * valid format (defaults to null).
22194      */
22195     minValue : null,
22196     /**
22197      * @cfg {Date/String} maxValue
22198      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22199      * valid format (defaults to null).
22200      */
22201     maxValue : null,
22202     /**
22203      * @cfg {String} minText
22204      * The error text to display when the date in the cell is before minValue (defaults to
22205      * 'The date in this field must be after {minValue}').
22206      */
22207     minText : "The date in this field must be equal to or after {0}",
22208     /**
22209      * @cfg {String} maxText
22210      * The error text to display when the date in the cell is after maxValue (defaults to
22211      * 'The date in this field must be before {maxValue}').
22212      */
22213     maxText : "The date in this field must be equal to or before {0}",
22214     /**
22215      * @cfg {String} invalidText
22216      * The error text to display when the date in the field is invalid (defaults to
22217      * '{value} is not a valid date - it must be in the format {format}').
22218      */
22219     invalidText : "{0} is not a valid date - it must be in the format {1}",
22220     /**
22221      * @cfg {String} triggerClass
22222      * An additional CSS class used to style the trigger button.  The trigger will always get the
22223      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22224      * which displays a calendar icon).
22225      */
22226     triggerClass : 'x-form-date-trigger',
22227     
22228
22229     /**
22230      * @cfg {Boolean} useIso
22231      * if enabled, then the date field will use a hidden field to store the 
22232      * real value as iso formated date. default (false)
22233      */ 
22234     useIso : false,
22235     /**
22236      * @cfg {String/Object} autoCreate
22237      * A DomHelper element spec, or true for a default element spec (defaults to
22238      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22239      */ 
22240     // private
22241     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22242     
22243     // private
22244     hiddenField: false,
22245     
22246     onRender : function(ct, position)
22247     {
22248         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22249         if (this.useIso) {
22250             //this.el.dom.removeAttribute('name'); 
22251             Roo.log("Changing name?");
22252             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22253             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22254                     'before', true);
22255             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22256             // prevent input submission
22257             this.hiddenName = this.name;
22258         }
22259             
22260             
22261     },
22262     
22263     // private
22264     validateValue : function(value)
22265     {
22266         value = this.formatDate(value);
22267         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22268             Roo.log('super failed');
22269             return false;
22270         }
22271         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22272              return true;
22273         }
22274         var svalue = value;
22275         value = this.parseDate(value);
22276         if(!value){
22277             Roo.log('parse date failed' + svalue);
22278             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22279             return false;
22280         }
22281         var time = value.getTime();
22282         if(this.minValue && time < this.minValue.getTime()){
22283             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22284             return false;
22285         }
22286         if(this.maxValue && time > this.maxValue.getTime()){
22287             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22288             return false;
22289         }
22290         if(this.disabledDays){
22291             var day = value.getDay();
22292             for(var i = 0; i < this.disabledDays.length; i++) {
22293                 if(day === this.disabledDays[i]){
22294                     this.markInvalid(this.disabledDaysText);
22295                     return false;
22296                 }
22297             }
22298         }
22299         var fvalue = this.formatDate(value);
22300         if(this.ddMatch && this.ddMatch.test(fvalue)){
22301             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22302             return false;
22303         }
22304         return true;
22305     },
22306
22307     // private
22308     // Provides logic to override the default TriggerField.validateBlur which just returns true
22309     validateBlur : function(){
22310         return !this.menu || !this.menu.isVisible();
22311     },
22312     
22313     getName: function()
22314     {
22315         // returns hidden if it's set..
22316         if (!this.rendered) {return ''};
22317         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22318         
22319     },
22320
22321     /**
22322      * Returns the current date value of the date field.
22323      * @return {Date} The date value
22324      */
22325     getValue : function(){
22326         
22327         return  this.hiddenField ?
22328                 this.hiddenField.value :
22329                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22330     },
22331
22332     /**
22333      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22334      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22335      * (the default format used is "m/d/y").
22336      * <br />Usage:
22337      * <pre><code>
22338 //All of these calls set the same date value (May 4, 2006)
22339
22340 //Pass a date object:
22341 var dt = new Date('5/4/06');
22342 dateField.setValue(dt);
22343
22344 //Pass a date string (default format):
22345 dateField.setValue('5/4/06');
22346
22347 //Pass a date string (custom format):
22348 dateField.format = 'Y-m-d';
22349 dateField.setValue('2006-5-4');
22350 </code></pre>
22351      * @param {String/Date} date The date or valid date string
22352      */
22353     setValue : function(date){
22354         if (this.hiddenField) {
22355             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22356         }
22357         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22358         // make sure the value field is always stored as a date..
22359         this.value = this.parseDate(date);
22360         
22361         
22362     },
22363
22364     // private
22365     parseDate : function(value){
22366         if(!value || value instanceof Date){
22367             return value;
22368         }
22369         var v = Date.parseDate(value, this.format);
22370          if (!v && this.useIso) {
22371             v = Date.parseDate(value, 'Y-m-d');
22372         }
22373         if(!v && this.altFormats){
22374             if(!this.altFormatsArray){
22375                 this.altFormatsArray = this.altFormats.split("|");
22376             }
22377             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22378                 v = Date.parseDate(value, this.altFormatsArray[i]);
22379             }
22380         }
22381         return v;
22382     },
22383
22384     // private
22385     formatDate : function(date, fmt){
22386         return (!date || !(date instanceof Date)) ?
22387                date : date.dateFormat(fmt || this.format);
22388     },
22389
22390     // private
22391     menuListeners : {
22392         select: function(m, d){
22393             
22394             this.setValue(d);
22395             this.fireEvent('select', this, d);
22396         },
22397         show : function(){ // retain focus styling
22398             this.onFocus();
22399         },
22400         hide : function(){
22401             this.focus.defer(10, this);
22402             var ml = this.menuListeners;
22403             this.menu.un("select", ml.select,  this);
22404             this.menu.un("show", ml.show,  this);
22405             this.menu.un("hide", ml.hide,  this);
22406         }
22407     },
22408
22409     // private
22410     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22411     onTriggerClick : function(){
22412         if(this.disabled){
22413             return;
22414         }
22415         if(this.menu == null){
22416             this.menu = new Roo.menu.DateMenu();
22417         }
22418         Roo.apply(this.menu.picker,  {
22419             showClear: this.allowBlank,
22420             minDate : this.minValue,
22421             maxDate : this.maxValue,
22422             disabledDatesRE : this.ddMatch,
22423             disabledDatesText : this.disabledDatesText,
22424             disabledDays : this.disabledDays,
22425             disabledDaysText : this.disabledDaysText,
22426             format : this.useIso ? 'Y-m-d' : this.format,
22427             minText : String.format(this.minText, this.formatDate(this.minValue)),
22428             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22429         });
22430         this.menu.on(Roo.apply({}, this.menuListeners, {
22431             scope:this
22432         }));
22433         this.menu.picker.setValue(this.getValue() || new Date());
22434         this.menu.show(this.el, "tl-bl?");
22435     },
22436
22437     beforeBlur : function(){
22438         var v = this.parseDate(this.getRawValue());
22439         if(v){
22440             this.setValue(v);
22441         }
22442     },
22443
22444     /*@
22445      * overide
22446      * 
22447      */
22448     isDirty : function() {
22449         if(this.disabled) {
22450             return false;
22451         }
22452         
22453         if(typeof(this.startValue) === 'undefined'){
22454             return false;
22455         }
22456         
22457         return String(this.getValue()) !== String(this.startValue);
22458         
22459     }
22460 });/*
22461  * Based on:
22462  * Ext JS Library 1.1.1
22463  * Copyright(c) 2006-2007, Ext JS, LLC.
22464  *
22465  * Originally Released Under LGPL - original licence link has changed is not relivant.
22466  *
22467  * Fork - LGPL
22468  * <script type="text/javascript">
22469  */
22470  
22471 /**
22472  * @class Roo.form.MonthField
22473  * @extends Roo.form.TriggerField
22474  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22475 * @constructor
22476 * Create a new MonthField
22477 * @param {Object} config
22478  */
22479 Roo.form.MonthField = function(config){
22480     
22481     Roo.form.MonthField.superclass.constructor.call(this, config);
22482     
22483       this.addEvents({
22484          
22485         /**
22486          * @event select
22487          * Fires when a date is selected
22488              * @param {Roo.form.MonthFieeld} combo This combo box
22489              * @param {Date} date The date selected
22490              */
22491         'select' : true
22492          
22493     });
22494     
22495     
22496     if(typeof this.minValue == "string") {
22497         this.minValue = this.parseDate(this.minValue);
22498     }
22499     if(typeof this.maxValue == "string") {
22500         this.maxValue = this.parseDate(this.maxValue);
22501     }
22502     this.ddMatch = null;
22503     if(this.disabledDates){
22504         var dd = this.disabledDates;
22505         var re = "(?:";
22506         for(var i = 0; i < dd.length; i++){
22507             re += dd[i];
22508             if(i != dd.length-1) {
22509                 re += "|";
22510             }
22511         }
22512         this.ddMatch = new RegExp(re + ")");
22513     }
22514 };
22515
22516 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22517     /**
22518      * @cfg {String} format
22519      * The default date format string which can be overriden for localization support.  The format must be
22520      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22521      */
22522     format : "M Y",
22523     /**
22524      * @cfg {String} altFormats
22525      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22526      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22527      */
22528     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22529     /**
22530      * @cfg {Array} disabledDays
22531      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22532      */
22533     disabledDays : [0,1,2,3,4,5,6],
22534     /**
22535      * @cfg {String} disabledDaysText
22536      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22537      */
22538     disabledDaysText : "Disabled",
22539     /**
22540      * @cfg {Array} disabledDates
22541      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22542      * expression so they are very powerful. Some examples:
22543      * <ul>
22544      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22545      * <li>["03/08", "09/16"] would disable those days for every year</li>
22546      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22547      * <li>["03/../2006"] would disable every day in March 2006</li>
22548      * <li>["^03"] would disable every day in every March</li>
22549      * </ul>
22550      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22551      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22552      */
22553     disabledDates : null,
22554     /**
22555      * @cfg {String} disabledDatesText
22556      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22557      */
22558     disabledDatesText : "Disabled",
22559     /**
22560      * @cfg {Date/String} minValue
22561      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22562      * valid format (defaults to null).
22563      */
22564     minValue : null,
22565     /**
22566      * @cfg {Date/String} maxValue
22567      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22568      * valid format (defaults to null).
22569      */
22570     maxValue : null,
22571     /**
22572      * @cfg {String} minText
22573      * The error text to display when the date in the cell is before minValue (defaults to
22574      * 'The date in this field must be after {minValue}').
22575      */
22576     minText : "The date in this field must be equal to or after {0}",
22577     /**
22578      * @cfg {String} maxTextf
22579      * The error text to display when the date in the cell is after maxValue (defaults to
22580      * 'The date in this field must be before {maxValue}').
22581      */
22582     maxText : "The date in this field must be equal to or before {0}",
22583     /**
22584      * @cfg {String} invalidText
22585      * The error text to display when the date in the field is invalid (defaults to
22586      * '{value} is not a valid date - it must be in the format {format}').
22587      */
22588     invalidText : "{0} is not a valid date - it must be in the format {1}",
22589     /**
22590      * @cfg {String} triggerClass
22591      * An additional CSS class used to style the trigger button.  The trigger will always get the
22592      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22593      * which displays a calendar icon).
22594      */
22595     triggerClass : 'x-form-date-trigger',
22596     
22597
22598     /**
22599      * @cfg {Boolean} useIso
22600      * if enabled, then the date field will use a hidden field to store the 
22601      * real value as iso formated date. default (true)
22602      */ 
22603     useIso : true,
22604     /**
22605      * @cfg {String/Object} autoCreate
22606      * A DomHelper element spec, or true for a default element spec (defaults to
22607      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22608      */ 
22609     // private
22610     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22611     
22612     // private
22613     hiddenField: false,
22614     
22615     hideMonthPicker : false,
22616     
22617     onRender : function(ct, position)
22618     {
22619         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22620         if (this.useIso) {
22621             this.el.dom.removeAttribute('name'); 
22622             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22623                     'before', true);
22624             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22625             // prevent input submission
22626             this.hiddenName = this.name;
22627         }
22628             
22629             
22630     },
22631     
22632     // private
22633     validateValue : function(value)
22634     {
22635         value = this.formatDate(value);
22636         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22637             return false;
22638         }
22639         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22640              return true;
22641         }
22642         var svalue = value;
22643         value = this.parseDate(value);
22644         if(!value){
22645             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22646             return false;
22647         }
22648         var time = value.getTime();
22649         if(this.minValue && time < this.minValue.getTime()){
22650             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22651             return false;
22652         }
22653         if(this.maxValue && time > this.maxValue.getTime()){
22654             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22655             return false;
22656         }
22657         /*if(this.disabledDays){
22658             var day = value.getDay();
22659             for(var i = 0; i < this.disabledDays.length; i++) {
22660                 if(day === this.disabledDays[i]){
22661                     this.markInvalid(this.disabledDaysText);
22662                     return false;
22663                 }
22664             }
22665         }
22666         */
22667         var fvalue = this.formatDate(value);
22668         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22669             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22670             return false;
22671         }
22672         */
22673         return true;
22674     },
22675
22676     // private
22677     // Provides logic to override the default TriggerField.validateBlur which just returns true
22678     validateBlur : function(){
22679         return !this.menu || !this.menu.isVisible();
22680     },
22681
22682     /**
22683      * Returns the current date value of the date field.
22684      * @return {Date} The date value
22685      */
22686     getValue : function(){
22687         
22688         
22689         
22690         return  this.hiddenField ?
22691                 this.hiddenField.value :
22692                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22693     },
22694
22695     /**
22696      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22697      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22698      * (the default format used is "m/d/y").
22699      * <br />Usage:
22700      * <pre><code>
22701 //All of these calls set the same date value (May 4, 2006)
22702
22703 //Pass a date object:
22704 var dt = new Date('5/4/06');
22705 monthField.setValue(dt);
22706
22707 //Pass a date string (default format):
22708 monthField.setValue('5/4/06');
22709
22710 //Pass a date string (custom format):
22711 monthField.format = 'Y-m-d';
22712 monthField.setValue('2006-5-4');
22713 </code></pre>
22714      * @param {String/Date} date The date or valid date string
22715      */
22716     setValue : function(date){
22717         Roo.log('month setValue' + date);
22718         // can only be first of month..
22719         
22720         var val = this.parseDate(date);
22721         
22722         if (this.hiddenField) {
22723             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22724         }
22725         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22726         this.value = this.parseDate(date);
22727     },
22728
22729     // private
22730     parseDate : function(value){
22731         if(!value || value instanceof Date){
22732             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22733             return value;
22734         }
22735         var v = Date.parseDate(value, this.format);
22736         if (!v && this.useIso) {
22737             v = Date.parseDate(value, 'Y-m-d');
22738         }
22739         if (v) {
22740             // 
22741             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22742         }
22743         
22744         
22745         if(!v && this.altFormats){
22746             if(!this.altFormatsArray){
22747                 this.altFormatsArray = this.altFormats.split("|");
22748             }
22749             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22750                 v = Date.parseDate(value, this.altFormatsArray[i]);
22751             }
22752         }
22753         return v;
22754     },
22755
22756     // private
22757     formatDate : function(date, fmt){
22758         return (!date || !(date instanceof Date)) ?
22759                date : date.dateFormat(fmt || this.format);
22760     },
22761
22762     // private
22763     menuListeners : {
22764         select: function(m, d){
22765             this.setValue(d);
22766             this.fireEvent('select', this, d);
22767         },
22768         show : function(){ // retain focus styling
22769             this.onFocus();
22770         },
22771         hide : function(){
22772             this.focus.defer(10, this);
22773             var ml = this.menuListeners;
22774             this.menu.un("select", ml.select,  this);
22775             this.menu.un("show", ml.show,  this);
22776             this.menu.un("hide", ml.hide,  this);
22777         }
22778     },
22779     // private
22780     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22781     onTriggerClick : function(){
22782         if(this.disabled){
22783             return;
22784         }
22785         if(this.menu == null){
22786             this.menu = new Roo.menu.DateMenu();
22787            
22788         }
22789         
22790         Roo.apply(this.menu.picker,  {
22791             
22792             showClear: this.allowBlank,
22793             minDate : this.minValue,
22794             maxDate : this.maxValue,
22795             disabledDatesRE : this.ddMatch,
22796             disabledDatesText : this.disabledDatesText,
22797             
22798             format : this.useIso ? 'Y-m-d' : this.format,
22799             minText : String.format(this.minText, this.formatDate(this.minValue)),
22800             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22801             
22802         });
22803          this.menu.on(Roo.apply({}, this.menuListeners, {
22804             scope:this
22805         }));
22806        
22807         
22808         var m = this.menu;
22809         var p = m.picker;
22810         
22811         // hide month picker get's called when we called by 'before hide';
22812         
22813         var ignorehide = true;
22814         p.hideMonthPicker  = function(disableAnim){
22815             if (ignorehide) {
22816                 return;
22817             }
22818              if(this.monthPicker){
22819                 Roo.log("hideMonthPicker called");
22820                 if(disableAnim === true){
22821                     this.monthPicker.hide();
22822                 }else{
22823                     this.monthPicker.slideOut('t', {duration:.2});
22824                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22825                     p.fireEvent("select", this, this.value);
22826                     m.hide();
22827                 }
22828             }
22829         }
22830         
22831         Roo.log('picker set value');
22832         Roo.log(this.getValue());
22833         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22834         m.show(this.el, 'tl-bl?');
22835         ignorehide  = false;
22836         // this will trigger hideMonthPicker..
22837         
22838         
22839         // hidden the day picker
22840         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22841         
22842         
22843         
22844       
22845         
22846         p.showMonthPicker.defer(100, p);
22847     
22848         
22849        
22850     },
22851
22852     beforeBlur : function(){
22853         var v = this.parseDate(this.getRawValue());
22854         if(v){
22855             this.setValue(v);
22856         }
22857     }
22858
22859     /** @cfg {Boolean} grow @hide */
22860     /** @cfg {Number} growMin @hide */
22861     /** @cfg {Number} growMax @hide */
22862     /**
22863      * @hide
22864      * @method autoSize
22865      */
22866 });/*
22867  * Based on:
22868  * Ext JS Library 1.1.1
22869  * Copyright(c) 2006-2007, Ext JS, LLC.
22870  *
22871  * Originally Released Under LGPL - original licence link has changed is not relivant.
22872  *
22873  * Fork - LGPL
22874  * <script type="text/javascript">
22875  */
22876  
22877
22878 /**
22879  * @class Roo.form.ComboBox
22880  * @extends Roo.form.TriggerField
22881  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22882  * @constructor
22883  * Create a new ComboBox.
22884  * @param {Object} config Configuration options
22885  */
22886 Roo.form.ComboBox = function(config){
22887     Roo.form.ComboBox.superclass.constructor.call(this, config);
22888     this.addEvents({
22889         /**
22890          * @event expand
22891          * Fires when the dropdown list is expanded
22892              * @param {Roo.form.ComboBox} combo This combo box
22893              */
22894         'expand' : true,
22895         /**
22896          * @event collapse
22897          * Fires when the dropdown list is collapsed
22898              * @param {Roo.form.ComboBox} combo This combo box
22899              */
22900         'collapse' : true,
22901         /**
22902          * @event beforeselect
22903          * Fires before a list item is selected. Return false to cancel the selection.
22904              * @param {Roo.form.ComboBox} combo This combo box
22905              * @param {Roo.data.Record} record The data record returned from the underlying store
22906              * @param {Number} index The index of the selected item in the dropdown list
22907              */
22908         'beforeselect' : true,
22909         /**
22910          * @event select
22911          * Fires when a list item is selected
22912              * @param {Roo.form.ComboBox} combo This combo box
22913              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22914              * @param {Number} index The index of the selected item in the dropdown list
22915              */
22916         'select' : true,
22917         /**
22918          * @event beforequery
22919          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22920          * The event object passed has these properties:
22921              * @param {Roo.form.ComboBox} combo This combo box
22922              * @param {String} query The query
22923              * @param {Boolean} forceAll true to force "all" query
22924              * @param {Boolean} cancel true to cancel the query
22925              * @param {Object} e The query event object
22926              */
22927         'beforequery': true,
22928          /**
22929          * @event add
22930          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22931              * @param {Roo.form.ComboBox} combo This combo box
22932              */
22933         'add' : true,
22934         /**
22935          * @event edit
22936          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22937              * @param {Roo.form.ComboBox} combo This combo box
22938              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22939              */
22940         'edit' : true
22941         
22942         
22943     });
22944     if(this.transform){
22945         this.allowDomMove = false;
22946         var s = Roo.getDom(this.transform);
22947         if(!this.hiddenName){
22948             this.hiddenName = s.name;
22949         }
22950         if(!this.store){
22951             this.mode = 'local';
22952             var d = [], opts = s.options;
22953             for(var i = 0, len = opts.length;i < len; i++){
22954                 var o = opts[i];
22955                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22956                 if(o.selected) {
22957                     this.value = value;
22958                 }
22959                 d.push([value, o.text]);
22960             }
22961             this.store = new Roo.data.SimpleStore({
22962                 'id': 0,
22963                 fields: ['value', 'text'],
22964                 data : d
22965             });
22966             this.valueField = 'value';
22967             this.displayField = 'text';
22968         }
22969         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22970         if(!this.lazyRender){
22971             this.target = true;
22972             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22973             s.parentNode.removeChild(s); // remove it
22974             this.render(this.el.parentNode);
22975         }else{
22976             s.parentNode.removeChild(s); // remove it
22977         }
22978
22979     }
22980     if (this.store) {
22981         this.store = Roo.factory(this.store, Roo.data);
22982     }
22983     
22984     this.selectedIndex = -1;
22985     if(this.mode == 'local'){
22986         if(config.queryDelay === undefined){
22987             this.queryDelay = 10;
22988         }
22989         if(config.minChars === undefined){
22990             this.minChars = 0;
22991         }
22992     }
22993 };
22994
22995 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22996     /**
22997      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22998      */
22999     /**
23000      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23001      * rendering into an Roo.Editor, defaults to false)
23002      */
23003     /**
23004      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23005      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23006      */
23007     /**
23008      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23009      */
23010     /**
23011      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23012      * the dropdown list (defaults to undefined, with no header element)
23013      */
23014
23015      /**
23016      * @cfg {String/Roo.Template} tpl The template to use to render the output
23017      */
23018      
23019     // private
23020     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23021     /**
23022      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23023      */
23024     listWidth: undefined,
23025     /**
23026      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23027      * mode = 'remote' or 'text' if mode = 'local')
23028      */
23029     displayField: undefined,
23030     /**
23031      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23032      * mode = 'remote' or 'value' if mode = 'local'). 
23033      * Note: use of a valueField requires the user make a selection
23034      * in order for a value to be mapped.
23035      */
23036     valueField: undefined,
23037     
23038     
23039     /**
23040      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23041      * field's data value (defaults to the underlying DOM element's name)
23042      */
23043     hiddenName: undefined,
23044     /**
23045      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23046      */
23047     listClass: '',
23048     /**
23049      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23050      */
23051     selectedClass: 'x-combo-selected',
23052     /**
23053      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23054      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23055      * which displays a downward arrow icon).
23056      */
23057     triggerClass : 'x-form-arrow-trigger',
23058     /**
23059      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23060      */
23061     shadow:'sides',
23062     /**
23063      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23064      * anchor positions (defaults to 'tl-bl')
23065      */
23066     listAlign: 'tl-bl?',
23067     /**
23068      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23069      */
23070     maxHeight: 300,
23071     /**
23072      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23073      * query specified by the allQuery config option (defaults to 'query')
23074      */
23075     triggerAction: 'query',
23076     /**
23077      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23078      * (defaults to 4, does not apply if editable = false)
23079      */
23080     minChars : 4,
23081     /**
23082      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23083      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23084      */
23085     typeAhead: false,
23086     /**
23087      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23088      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23089      */
23090     queryDelay: 500,
23091     /**
23092      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23093      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23094      */
23095     pageSize: 0,
23096     /**
23097      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23098      * when editable = true (defaults to false)
23099      */
23100     selectOnFocus:false,
23101     /**
23102      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23103      */
23104     queryParam: 'query',
23105     /**
23106      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23107      * when mode = 'remote' (defaults to 'Loading...')
23108      */
23109     loadingText: 'Loading...',
23110     /**
23111      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23112      */
23113     resizable: false,
23114     /**
23115      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23116      */
23117     handleHeight : 8,
23118     /**
23119      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23120      * traditional select (defaults to true)
23121      */
23122     editable: true,
23123     /**
23124      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23125      */
23126     allQuery: '',
23127     /**
23128      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23129      */
23130     mode: 'remote',
23131     /**
23132      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23133      * listWidth has a higher value)
23134      */
23135     minListWidth : 70,
23136     /**
23137      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23138      * allow the user to set arbitrary text into the field (defaults to false)
23139      */
23140     forceSelection:false,
23141     /**
23142      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23143      * if typeAhead = true (defaults to 250)
23144      */
23145     typeAheadDelay : 250,
23146     /**
23147      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23148      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23149      */
23150     valueNotFoundText : undefined,
23151     /**
23152      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23153      */
23154     blockFocus : false,
23155     
23156     /**
23157      * @cfg {Boolean} disableClear Disable showing of clear button.
23158      */
23159     disableClear : false,
23160     /**
23161      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23162      */
23163     alwaysQuery : false,
23164     
23165     //private
23166     addicon : false,
23167     editicon: false,
23168     
23169     // element that contains real text value.. (when hidden is used..)
23170      
23171     // private
23172     onRender : function(ct, position){
23173         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23174         if(this.hiddenName){
23175             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23176                     'before', true);
23177             this.hiddenField.value =
23178                 this.hiddenValue !== undefined ? this.hiddenValue :
23179                 this.value !== undefined ? this.value : '';
23180
23181             // prevent input submission
23182             this.el.dom.removeAttribute('name');
23183              
23184              
23185         }
23186         if(Roo.isGecko){
23187             this.el.dom.setAttribute('autocomplete', 'off');
23188         }
23189
23190         var cls = 'x-combo-list';
23191
23192         this.list = new Roo.Layer({
23193             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23194         });
23195
23196         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23197         this.list.setWidth(lw);
23198         this.list.swallowEvent('mousewheel');
23199         this.assetHeight = 0;
23200
23201         if(this.title){
23202             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23203             this.assetHeight += this.header.getHeight();
23204         }
23205
23206         this.innerList = this.list.createChild({cls:cls+'-inner'});
23207         this.innerList.on('mouseover', this.onViewOver, this);
23208         this.innerList.on('mousemove', this.onViewMove, this);
23209         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23210         
23211         if(this.allowBlank && !this.pageSize && !this.disableClear){
23212             this.footer = this.list.createChild({cls:cls+'-ft'});
23213             this.pageTb = new Roo.Toolbar(this.footer);
23214            
23215         }
23216         if(this.pageSize){
23217             this.footer = this.list.createChild({cls:cls+'-ft'});
23218             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23219                     {pageSize: this.pageSize});
23220             
23221         }
23222         
23223         if (this.pageTb && this.allowBlank && !this.disableClear) {
23224             var _this = this;
23225             this.pageTb.add(new Roo.Toolbar.Fill(), {
23226                 cls: 'x-btn-icon x-btn-clear',
23227                 text: '&#160;',
23228                 handler: function()
23229                 {
23230                     _this.collapse();
23231                     _this.clearValue();
23232                     _this.onSelect(false, -1);
23233                 }
23234             });
23235         }
23236         if (this.footer) {
23237             this.assetHeight += this.footer.getHeight();
23238         }
23239         
23240
23241         if(!this.tpl){
23242             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23243         }
23244
23245         this.view = new Roo.View(this.innerList, this.tpl, {
23246             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23247         });
23248
23249         this.view.on('click', this.onViewClick, this);
23250
23251         this.store.on('beforeload', this.onBeforeLoad, this);
23252         this.store.on('load', this.onLoad, this);
23253         this.store.on('loadexception', this.onLoadException, this);
23254
23255         if(this.resizable){
23256             this.resizer = new Roo.Resizable(this.list,  {
23257                pinned:true, handles:'se'
23258             });
23259             this.resizer.on('resize', function(r, w, h){
23260                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23261                 this.listWidth = w;
23262                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23263                 this.restrictHeight();
23264             }, this);
23265             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23266         }
23267         if(!this.editable){
23268             this.editable = true;
23269             this.setEditable(false);
23270         }  
23271         
23272         
23273         if (typeof(this.events.add.listeners) != 'undefined') {
23274             
23275             this.addicon = this.wrap.createChild(
23276                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23277        
23278             this.addicon.on('click', function(e) {
23279                 this.fireEvent('add', this);
23280             }, this);
23281         }
23282         if (typeof(this.events.edit.listeners) != 'undefined') {
23283             
23284             this.editicon = this.wrap.createChild(
23285                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23286             if (this.addicon) {
23287                 this.editicon.setStyle('margin-left', '40px');
23288             }
23289             this.editicon.on('click', function(e) {
23290                 
23291                 // we fire even  if inothing is selected..
23292                 this.fireEvent('edit', this, this.lastData );
23293                 
23294             }, this);
23295         }
23296         
23297         
23298         
23299     },
23300
23301     // private
23302     initEvents : function(){
23303         Roo.form.ComboBox.superclass.initEvents.call(this);
23304
23305         this.keyNav = new Roo.KeyNav(this.el, {
23306             "up" : function(e){
23307                 this.inKeyMode = true;
23308                 this.selectPrev();
23309             },
23310
23311             "down" : function(e){
23312                 if(!this.isExpanded()){
23313                     this.onTriggerClick();
23314                 }else{
23315                     this.inKeyMode = true;
23316                     this.selectNext();
23317                 }
23318             },
23319
23320             "enter" : function(e){
23321                 this.onViewClick();
23322                 //return true;
23323             },
23324
23325             "esc" : function(e){
23326                 this.collapse();
23327             },
23328
23329             "tab" : function(e){
23330                 this.onViewClick(false);
23331                 this.fireEvent("specialkey", this, e);
23332                 return true;
23333             },
23334
23335             scope : this,
23336
23337             doRelay : function(foo, bar, hname){
23338                 if(hname == 'down' || this.scope.isExpanded()){
23339                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23340                 }
23341                 return true;
23342             },
23343
23344             forceKeyDown: true
23345         });
23346         this.queryDelay = Math.max(this.queryDelay || 10,
23347                 this.mode == 'local' ? 10 : 250);
23348         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23349         if(this.typeAhead){
23350             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23351         }
23352         if(this.editable !== false){
23353             this.el.on("keyup", this.onKeyUp, this);
23354         }
23355         if(this.forceSelection){
23356             this.on('blur', this.doForce, this);
23357         }
23358     },
23359
23360     onDestroy : function(){
23361         if(this.view){
23362             this.view.setStore(null);
23363             this.view.el.removeAllListeners();
23364             this.view.el.remove();
23365             this.view.purgeListeners();
23366         }
23367         if(this.list){
23368             this.list.destroy();
23369         }
23370         if(this.store){
23371             this.store.un('beforeload', this.onBeforeLoad, this);
23372             this.store.un('load', this.onLoad, this);
23373             this.store.un('loadexception', this.onLoadException, this);
23374         }
23375         Roo.form.ComboBox.superclass.onDestroy.call(this);
23376     },
23377
23378     // private
23379     fireKey : function(e){
23380         if(e.isNavKeyPress() && !this.list.isVisible()){
23381             this.fireEvent("specialkey", this, e);
23382         }
23383     },
23384
23385     // private
23386     onResize: function(w, h){
23387         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23388         
23389         if(typeof w != 'number'){
23390             // we do not handle it!?!?
23391             return;
23392         }
23393         var tw = this.trigger.getWidth();
23394         tw += this.addicon ? this.addicon.getWidth() : 0;
23395         tw += this.editicon ? this.editicon.getWidth() : 0;
23396         var x = w - tw;
23397         this.el.setWidth( this.adjustWidth('input', x));
23398             
23399         this.trigger.setStyle('left', x+'px');
23400         
23401         if(this.list && this.listWidth === undefined){
23402             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23403             this.list.setWidth(lw);
23404             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23405         }
23406         
23407     
23408         
23409     },
23410
23411     /**
23412      * Allow or prevent the user from directly editing the field text.  If false is passed,
23413      * the user will only be able to select from the items defined in the dropdown list.  This method
23414      * is the runtime equivalent of setting the 'editable' config option at config time.
23415      * @param {Boolean} value True to allow the user to directly edit the field text
23416      */
23417     setEditable : function(value){
23418         if(value == this.editable){
23419             return;
23420         }
23421         this.editable = value;
23422         if(!value){
23423             this.el.dom.setAttribute('readOnly', true);
23424             this.el.on('mousedown', this.onTriggerClick,  this);
23425             this.el.addClass('x-combo-noedit');
23426         }else{
23427             this.el.dom.setAttribute('readOnly', false);
23428             this.el.un('mousedown', this.onTriggerClick,  this);
23429             this.el.removeClass('x-combo-noedit');
23430         }
23431     },
23432
23433     // private
23434     onBeforeLoad : function(){
23435         if(!this.hasFocus){
23436             return;
23437         }
23438         this.innerList.update(this.loadingText ?
23439                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23440         this.restrictHeight();
23441         this.selectedIndex = -1;
23442     },
23443
23444     // private
23445     onLoad : function(){
23446         if(!this.hasFocus){
23447             return;
23448         }
23449         if(this.store.getCount() > 0){
23450             this.expand();
23451             this.restrictHeight();
23452             if(this.lastQuery == this.allQuery){
23453                 if(this.editable){
23454                     this.el.dom.select();
23455                 }
23456                 if(!this.selectByValue(this.value, true)){
23457                     this.select(0, true);
23458                 }
23459             }else{
23460                 this.selectNext();
23461                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23462                     this.taTask.delay(this.typeAheadDelay);
23463                 }
23464             }
23465         }else{
23466             this.onEmptyResults();
23467         }
23468         //this.el.focus();
23469     },
23470     // private
23471     onLoadException : function()
23472     {
23473         this.collapse();
23474         Roo.log(this.store.reader.jsonData);
23475         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23476             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23477         }
23478         
23479         
23480     },
23481     // private
23482     onTypeAhead : function(){
23483         if(this.store.getCount() > 0){
23484             var r = this.store.getAt(0);
23485             var newValue = r.data[this.displayField];
23486             var len = newValue.length;
23487             var selStart = this.getRawValue().length;
23488             if(selStart != len){
23489                 this.setRawValue(newValue);
23490                 this.selectText(selStart, newValue.length);
23491             }
23492         }
23493     },
23494
23495     // private
23496     onSelect : function(record, index){
23497         if(this.fireEvent('beforeselect', this, record, index) !== false){
23498             this.setFromData(index > -1 ? record.data : false);
23499             this.collapse();
23500             this.fireEvent('select', this, record, index);
23501         }
23502     },
23503
23504     /**
23505      * Returns the currently selected field value or empty string if no value is set.
23506      * @return {String} value The selected value
23507      */
23508     getValue : function(){
23509         if(this.valueField){
23510             return typeof this.value != 'undefined' ? this.value : '';
23511         }
23512         return Roo.form.ComboBox.superclass.getValue.call(this);
23513     },
23514
23515     /**
23516      * Clears any text/value currently set in the field
23517      */
23518     clearValue : function(){
23519         if(this.hiddenField){
23520             this.hiddenField.value = '';
23521         }
23522         this.value = '';
23523         this.setRawValue('');
23524         this.lastSelectionText = '';
23525         
23526     },
23527
23528     /**
23529      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23530      * will be displayed in the field.  If the value does not match the data value of an existing item,
23531      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23532      * Otherwise the field will be blank (although the value will still be set).
23533      * @param {String} value The value to match
23534      */
23535     setValue : function(v){
23536         var text = v;
23537         if(this.valueField){
23538             var r = this.findRecord(this.valueField, v);
23539             if(r){
23540                 text = r.data[this.displayField];
23541             }else if(this.valueNotFoundText !== undefined){
23542                 text = this.valueNotFoundText;
23543             }
23544         }
23545         this.lastSelectionText = text;
23546         if(this.hiddenField){
23547             this.hiddenField.value = v;
23548         }
23549         Roo.form.ComboBox.superclass.setValue.call(this, text);
23550         this.value = v;
23551     },
23552     /**
23553      * @property {Object} the last set data for the element
23554      */
23555     
23556     lastData : false,
23557     /**
23558      * Sets the value of the field based on a object which is related to the record format for the store.
23559      * @param {Object} value the value to set as. or false on reset?
23560      */
23561     setFromData : function(o){
23562         var dv = ''; // display value
23563         var vv = ''; // value value..
23564         this.lastData = o;
23565         if (this.displayField) {
23566             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23567         } else {
23568             // this is an error condition!!!
23569             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23570         }
23571         
23572         if(this.valueField){
23573             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23574         }
23575         if(this.hiddenField){
23576             this.hiddenField.value = vv;
23577             
23578             this.lastSelectionText = dv;
23579             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23580             this.value = vv;
23581             return;
23582         }
23583         // no hidden field.. - we store the value in 'value', but still display
23584         // display field!!!!
23585         this.lastSelectionText = dv;
23586         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23587         this.value = vv;
23588         
23589         
23590     },
23591     // private
23592     reset : function(){
23593         // overridden so that last data is reset..
23594         this.setValue(this.resetValue);
23595         this.clearInvalid();
23596         this.lastData = false;
23597         if (this.view) {
23598             this.view.clearSelections();
23599         }
23600     },
23601     // private
23602     findRecord : function(prop, value){
23603         var record;
23604         if(this.store.getCount() > 0){
23605             this.store.each(function(r){
23606                 if(r.data[prop] == value){
23607                     record = r;
23608                     return false;
23609                 }
23610                 return true;
23611             });
23612         }
23613         return record;
23614     },
23615     
23616     getName: function()
23617     {
23618         // returns hidden if it's set..
23619         if (!this.rendered) {return ''};
23620         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23621         
23622     },
23623     // private
23624     onViewMove : function(e, t){
23625         this.inKeyMode = false;
23626     },
23627
23628     // private
23629     onViewOver : function(e, t){
23630         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23631             return;
23632         }
23633         var item = this.view.findItemFromChild(t);
23634         if(item){
23635             var index = this.view.indexOf(item);
23636             this.select(index, false);
23637         }
23638     },
23639
23640     // private
23641     onViewClick : function(doFocus)
23642     {
23643         var index = this.view.getSelectedIndexes()[0];
23644         var r = this.store.getAt(index);
23645         if(r){
23646             this.onSelect(r, index);
23647         }
23648         if(doFocus !== false && !this.blockFocus){
23649             this.el.focus();
23650         }
23651     },
23652
23653     // private
23654     restrictHeight : function(){
23655         this.innerList.dom.style.height = '';
23656         var inner = this.innerList.dom;
23657         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23658         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23659         this.list.beginUpdate();
23660         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23661         this.list.alignTo(this.el, this.listAlign);
23662         this.list.endUpdate();
23663     },
23664
23665     // private
23666     onEmptyResults : function(){
23667         this.collapse();
23668     },
23669
23670     /**
23671      * Returns true if the dropdown list is expanded, else false.
23672      */
23673     isExpanded : function(){
23674         return this.list.isVisible();
23675     },
23676
23677     /**
23678      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23679      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23680      * @param {String} value The data value of the item to select
23681      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23682      * selected item if it is not currently in view (defaults to true)
23683      * @return {Boolean} True if the value matched an item in the list, else false
23684      */
23685     selectByValue : function(v, scrollIntoView){
23686         if(v !== undefined && v !== null){
23687             var r = this.findRecord(this.valueField || this.displayField, v);
23688             if(r){
23689                 this.select(this.store.indexOf(r), scrollIntoView);
23690                 return true;
23691             }
23692         }
23693         return false;
23694     },
23695
23696     /**
23697      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23698      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23699      * @param {Number} index The zero-based index of the list item to select
23700      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23701      * selected item if it is not currently in view (defaults to true)
23702      */
23703     select : function(index, scrollIntoView){
23704         this.selectedIndex = index;
23705         this.view.select(index);
23706         if(scrollIntoView !== false){
23707             var el = this.view.getNode(index);
23708             if(el){
23709                 this.innerList.scrollChildIntoView(el, false);
23710             }
23711         }
23712     },
23713
23714     // private
23715     selectNext : function(){
23716         var ct = this.store.getCount();
23717         if(ct > 0){
23718             if(this.selectedIndex == -1){
23719                 this.select(0);
23720             }else if(this.selectedIndex < ct-1){
23721                 this.select(this.selectedIndex+1);
23722             }
23723         }
23724     },
23725
23726     // private
23727     selectPrev : function(){
23728         var ct = this.store.getCount();
23729         if(ct > 0){
23730             if(this.selectedIndex == -1){
23731                 this.select(0);
23732             }else if(this.selectedIndex != 0){
23733                 this.select(this.selectedIndex-1);
23734             }
23735         }
23736     },
23737
23738     // private
23739     onKeyUp : function(e){
23740         if(this.editable !== false && !e.isSpecialKey()){
23741             this.lastKey = e.getKey();
23742             this.dqTask.delay(this.queryDelay);
23743         }
23744     },
23745
23746     // private
23747     validateBlur : function(){
23748         return !this.list || !this.list.isVisible();   
23749     },
23750
23751     // private
23752     initQuery : function(){
23753         this.doQuery(this.getRawValue());
23754     },
23755
23756     // private
23757     doForce : function(){
23758         if(this.el.dom.value.length > 0){
23759             this.el.dom.value =
23760                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23761              
23762         }
23763     },
23764
23765     /**
23766      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23767      * query allowing the query action to be canceled if needed.
23768      * @param {String} query The SQL query to execute
23769      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23770      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23771      * saved in the current store (defaults to false)
23772      */
23773     doQuery : function(q, forceAll){
23774         if(q === undefined || q === null){
23775             q = '';
23776         }
23777         var qe = {
23778             query: q,
23779             forceAll: forceAll,
23780             combo: this,
23781             cancel:false
23782         };
23783         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23784             return false;
23785         }
23786         q = qe.query;
23787         forceAll = qe.forceAll;
23788         if(forceAll === true || (q.length >= this.minChars)){
23789             if(this.lastQuery != q || this.alwaysQuery){
23790                 this.lastQuery = q;
23791                 if(this.mode == 'local'){
23792                     this.selectedIndex = -1;
23793                     if(forceAll){
23794                         this.store.clearFilter();
23795                     }else{
23796                         this.store.filter(this.displayField, q);
23797                     }
23798                     this.onLoad();
23799                 }else{
23800                     this.store.baseParams[this.queryParam] = q;
23801                     this.store.load({
23802                         params: this.getParams(q)
23803                     });
23804                     this.expand();
23805                 }
23806             }else{
23807                 this.selectedIndex = -1;
23808                 this.onLoad();   
23809             }
23810         }
23811     },
23812
23813     // private
23814     getParams : function(q){
23815         var p = {};
23816         //p[this.queryParam] = q;
23817         if(this.pageSize){
23818             p.start = 0;
23819             p.limit = this.pageSize;
23820         }
23821         return p;
23822     },
23823
23824     /**
23825      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23826      */
23827     collapse : function(){
23828         if(!this.isExpanded()){
23829             return;
23830         }
23831         this.list.hide();
23832         Roo.get(document).un('mousedown', this.collapseIf, this);
23833         Roo.get(document).un('mousewheel', this.collapseIf, this);
23834         if (!this.editable) {
23835             Roo.get(document).un('keydown', this.listKeyPress, this);
23836         }
23837         this.fireEvent('collapse', this);
23838     },
23839
23840     // private
23841     collapseIf : function(e){
23842         if(!e.within(this.wrap) && !e.within(this.list)){
23843             this.collapse();
23844         }
23845     },
23846
23847     /**
23848      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23849      */
23850     expand : function(){
23851         if(this.isExpanded() || !this.hasFocus){
23852             return;
23853         }
23854         this.list.alignTo(this.el, this.listAlign);
23855         this.list.show();
23856         Roo.get(document).on('mousedown', this.collapseIf, this);
23857         Roo.get(document).on('mousewheel', this.collapseIf, this);
23858         if (!this.editable) {
23859             Roo.get(document).on('keydown', this.listKeyPress, this);
23860         }
23861         
23862         this.fireEvent('expand', this);
23863     },
23864
23865     // private
23866     // Implements the default empty TriggerField.onTriggerClick function
23867     onTriggerClick : function(){
23868         if(this.disabled){
23869             return;
23870         }
23871         if(this.isExpanded()){
23872             this.collapse();
23873             if (!this.blockFocus) {
23874                 this.el.focus();
23875             }
23876             
23877         }else {
23878             this.hasFocus = true;
23879             if(this.triggerAction == 'all') {
23880                 this.doQuery(this.allQuery, true);
23881             } else {
23882                 this.doQuery(this.getRawValue());
23883             }
23884             if (!this.blockFocus) {
23885                 this.el.focus();
23886             }
23887         }
23888     },
23889     listKeyPress : function(e)
23890     {
23891         //Roo.log('listkeypress');
23892         // scroll to first matching element based on key pres..
23893         if (e.isSpecialKey()) {
23894             return false;
23895         }
23896         var k = String.fromCharCode(e.getKey()).toUpperCase();
23897         //Roo.log(k);
23898         var match  = false;
23899         var csel = this.view.getSelectedNodes();
23900         var cselitem = false;
23901         if (csel.length) {
23902             var ix = this.view.indexOf(csel[0]);
23903             cselitem  = this.store.getAt(ix);
23904             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23905                 cselitem = false;
23906             }
23907             
23908         }
23909         
23910         this.store.each(function(v) { 
23911             if (cselitem) {
23912                 // start at existing selection.
23913                 if (cselitem.id == v.id) {
23914                     cselitem = false;
23915                 }
23916                 return;
23917             }
23918                 
23919             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23920                 match = this.store.indexOf(v);
23921                 return false;
23922             }
23923         }, this);
23924         
23925         if (match === false) {
23926             return true; // no more action?
23927         }
23928         // scroll to?
23929         this.view.select(match);
23930         var sn = Roo.get(this.view.getSelectedNodes()[0]);
23931         sn.scrollIntoView(sn.dom.parentNode, false);
23932     }
23933
23934     /** 
23935     * @cfg {Boolean} grow 
23936     * @hide 
23937     */
23938     /** 
23939     * @cfg {Number} growMin 
23940     * @hide 
23941     */
23942     /** 
23943     * @cfg {Number} growMax 
23944     * @hide 
23945     */
23946     /**
23947      * @hide
23948      * @method autoSize
23949      */
23950 });/*
23951  * Copyright(c) 2010-2012, Roo J Solutions Limited
23952  *
23953  * Licence LGPL
23954  *
23955  */
23956
23957 /**
23958  * @class Roo.form.ComboBoxArray
23959  * @extends Roo.form.TextField
23960  * A facebook style adder... for lists of email / people / countries  etc...
23961  * pick multiple items from a combo box, and shows each one.
23962  *
23963  *  Fred [x]  Brian [x]  [Pick another |v]
23964  *
23965  *
23966  *  For this to work: it needs various extra information
23967  *    - normal combo problay has
23968  *      name, hiddenName
23969  *    + displayField, valueField
23970  *
23971  *    For our purpose...
23972  *
23973  *
23974  *   If we change from 'extends' to wrapping...
23975  *   
23976  *  
23977  *
23978  
23979  
23980  * @constructor
23981  * Create a new ComboBoxArray.
23982  * @param {Object} config Configuration options
23983  */
23984  
23985
23986 Roo.form.ComboBoxArray = function(config)
23987 {
23988     this.addEvents({
23989         /**
23990          * @event beforeremove
23991          * Fires before remove the value from the list
23992              * @param {Roo.form.ComboBoxArray} _self This combo box array
23993              * @param {Roo.form.ComboBoxArray.Item} item removed item
23994              */
23995         'beforeremove' : true,
23996         /**
23997          * @event remove
23998          * Fires when remove the value from the list
23999              * @param {Roo.form.ComboBoxArray} _self This combo box array
24000              * @param {Roo.form.ComboBoxArray.Item} item removed item
24001              */
24002         'remove' : true
24003         
24004         
24005     });
24006     
24007     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24008     
24009     this.items = new Roo.util.MixedCollection(false);
24010     
24011     // construct the child combo...
24012     
24013     
24014     
24015     
24016    
24017     
24018 }
24019
24020  
24021 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24022
24023     /**
24024      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24025      */
24026     
24027     lastData : false,
24028     
24029     // behavies liek a hiddne field
24030     inputType:      'hidden',
24031     /**
24032      * @cfg {Number} width The width of the box that displays the selected element
24033      */ 
24034     width:          300,
24035
24036     
24037     
24038     /**
24039      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24040      */
24041     name : false,
24042     /**
24043      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24044      */
24045     hiddenName : false,
24046     
24047     
24048     // private the array of items that are displayed..
24049     items  : false,
24050     // private - the hidden field el.
24051     hiddenEl : false,
24052     // private - the filed el..
24053     el : false,
24054     
24055     //validateValue : function() { return true; }, // all values are ok!
24056     //onAddClick: function() { },
24057     
24058     onRender : function(ct, position) 
24059     {
24060         
24061         // create the standard hidden element
24062         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24063         
24064         
24065         // give fake names to child combo;
24066         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24067         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24068         
24069         this.combo = Roo.factory(this.combo, Roo.form);
24070         this.combo.onRender(ct, position);
24071         if (typeof(this.combo.width) != 'undefined') {
24072             this.combo.onResize(this.combo.width,0);
24073         }
24074         
24075         this.combo.initEvents();
24076         
24077         // assigned so form know we need to do this..
24078         this.store          = this.combo.store;
24079         this.valueField     = this.combo.valueField;
24080         this.displayField   = this.combo.displayField ;
24081         
24082         
24083         this.combo.wrap.addClass('x-cbarray-grp');
24084         
24085         var cbwrap = this.combo.wrap.createChild(
24086             {tag: 'div', cls: 'x-cbarray-cb'},
24087             this.combo.el.dom
24088         );
24089         
24090              
24091         this.hiddenEl = this.combo.wrap.createChild({
24092             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24093         });
24094         this.el = this.combo.wrap.createChild({
24095             tag: 'input',  type:'hidden' , name: this.name, value : ''
24096         });
24097          //   this.el.dom.removeAttribute("name");
24098         
24099         
24100         this.outerWrap = this.combo.wrap;
24101         this.wrap = cbwrap;
24102         
24103         this.outerWrap.setWidth(this.width);
24104         this.outerWrap.dom.removeChild(this.el.dom);
24105         
24106         this.wrap.dom.appendChild(this.el.dom);
24107         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24108         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24109         
24110         this.combo.trigger.setStyle('position','relative');
24111         this.combo.trigger.setStyle('left', '0px');
24112         this.combo.trigger.setStyle('top', '2px');
24113         
24114         this.combo.el.setStyle('vertical-align', 'text-bottom');
24115         
24116         //this.trigger.setStyle('vertical-align', 'top');
24117         
24118         // this should use the code from combo really... on('add' ....)
24119         if (this.adder) {
24120             
24121         
24122             this.adder = this.outerWrap.createChild(
24123                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24124             var _t = this;
24125             this.adder.on('click', function(e) {
24126                 _t.fireEvent('adderclick', this, e);
24127             }, _t);
24128         }
24129         //var _t = this;
24130         //this.adder.on('click', this.onAddClick, _t);
24131         
24132         
24133         this.combo.on('select', function(cb, rec, ix) {
24134             this.addItem(rec.data);
24135             
24136             cb.setValue('');
24137             cb.el.dom.value = '';
24138             //cb.lastData = rec.data;
24139             // add to list
24140             
24141         }, this);
24142         
24143         
24144     },
24145     
24146     
24147     getName: function()
24148     {
24149         // returns hidden if it's set..
24150         if (!this.rendered) {return ''};
24151         return  this.hiddenName ? this.hiddenName : this.name;
24152         
24153     },
24154     
24155     
24156     onResize: function(w, h){
24157         
24158         return;
24159         // not sure if this is needed..
24160         //this.combo.onResize(w,h);
24161         
24162         if(typeof w != 'number'){
24163             // we do not handle it!?!?
24164             return;
24165         }
24166         var tw = this.combo.trigger.getWidth();
24167         tw += this.addicon ? this.addicon.getWidth() : 0;
24168         tw += this.editicon ? this.editicon.getWidth() : 0;
24169         var x = w - tw;
24170         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24171             
24172         this.combo.trigger.setStyle('left', '0px');
24173         
24174         if(this.list && this.listWidth === undefined){
24175             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24176             this.list.setWidth(lw);
24177             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24178         }
24179         
24180     
24181         
24182     },
24183     
24184     addItem: function(rec)
24185     {
24186         var valueField = this.combo.valueField;
24187         var displayField = this.combo.displayField;
24188         if (this.items.indexOfKey(rec[valueField]) > -1) {
24189             //console.log("GOT " + rec.data.id);
24190             return;
24191         }
24192         
24193         var x = new Roo.form.ComboBoxArray.Item({
24194             //id : rec[this.idField],
24195             data : rec,
24196             displayField : displayField ,
24197             tipField : displayField ,
24198             cb : this
24199         });
24200         // use the 
24201         this.items.add(rec[valueField],x);
24202         // add it before the element..
24203         this.updateHiddenEl();
24204         x.render(this.outerWrap, this.wrap.dom);
24205         // add the image handler..
24206     },
24207     
24208     updateHiddenEl : function()
24209     {
24210         this.validate();
24211         if (!this.hiddenEl) {
24212             return;
24213         }
24214         var ar = [];
24215         var idField = this.combo.valueField;
24216         
24217         this.items.each(function(f) {
24218             ar.push(f.data[idField]);
24219            
24220         });
24221         this.hiddenEl.dom.value = ar.join(',');
24222         this.validate();
24223     },
24224     
24225     reset : function()
24226     {
24227         this.items.clear();
24228         
24229         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
24230            el.remove();
24231         });
24232         
24233         this.el.dom.value = '';
24234         if (this.hiddenEl) {
24235             this.hiddenEl.dom.value = '';
24236         }
24237         
24238     },
24239     getValue: function()
24240     {
24241         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24242     },
24243     setValue: function(v) // not a valid action - must use addItems..
24244     {
24245          
24246         this.reset();
24247         
24248         
24249         
24250         if (this.store.isLocal && (typeof(v) == 'string')) {
24251             // then we can use the store to find the values..
24252             // comma seperated at present.. this needs to allow JSON based encoding..
24253             this.hiddenEl.value  = v;
24254             var v_ar = [];
24255             Roo.each(v.split(','), function(k) {
24256                 Roo.log("CHECK " + this.valueField + ',' + k);
24257                 var li = this.store.query(this.valueField, k);
24258                 if (!li.length) {
24259                     return;
24260                 }
24261                 var add = {};
24262                 add[this.valueField] = k;
24263                 add[this.displayField] = li.item(0).data[this.displayField];
24264                 
24265                 this.addItem(add);
24266             }, this) 
24267              
24268         }
24269         if (typeof(v) == 'object' ) {
24270             // then let's assume it's an array of objects..
24271             Roo.each(v, function(l) {
24272                 this.addItem(l);
24273             }, this);
24274              
24275         }
24276         
24277         
24278     },
24279     setFromData: function(v)
24280     {
24281         // this recieves an object, if setValues is called.
24282         this.reset();
24283         this.el.dom.value = v[this.displayField];
24284         this.hiddenEl.dom.value = v[this.valueField];
24285         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24286             return;
24287         }
24288         var kv = v[this.valueField];
24289         var dv = v[this.displayField];
24290         kv = typeof(kv) != 'string' ? '' : kv;
24291         dv = typeof(dv) != 'string' ? '' : dv;
24292         
24293         
24294         var keys = kv.split(',');
24295         var display = dv.split(',');
24296         for (var i = 0 ; i < keys.length; i++) {
24297             
24298             add = {};
24299             add[this.valueField] = keys[i];
24300             add[this.displayField] = display[i];
24301             this.addItem(add);
24302         }
24303       
24304         
24305     },
24306     
24307     /**
24308      * Validates the combox array value
24309      * @return {Boolean} True if the value is valid, else false
24310      */
24311     validate : function(){
24312         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24313             this.clearInvalid();
24314             return true;
24315         }
24316         return false;
24317     },
24318     
24319     validateValue : function(value){
24320         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24321         
24322     },
24323     
24324     /*@
24325      * overide
24326      * 
24327      */
24328     isDirty : function() {
24329         if(this.disabled) {
24330             return false;
24331         }
24332         
24333         try {
24334             var d = Roo.decode(String(this.originalValue));
24335         } catch (e) {
24336             return String(this.getValue()) !== String(this.originalValue);
24337         }
24338         
24339         var originalValue = [];
24340         
24341         for (var i = 0; i < d.length; i++){
24342             originalValue.push(d[i][this.valueField]);
24343         }
24344         
24345         return String(this.getValue()) !== String(originalValue.join(','));
24346         
24347     }
24348     
24349 });
24350
24351
24352
24353 /**
24354  * @class Roo.form.ComboBoxArray.Item
24355  * @extends Roo.BoxComponent
24356  * A selected item in the list
24357  *  Fred [x]  Brian [x]  [Pick another |v]
24358  * 
24359  * @constructor
24360  * Create a new item.
24361  * @param {Object} config Configuration options
24362  */
24363  
24364 Roo.form.ComboBoxArray.Item = function(config) {
24365     config.id = Roo.id();
24366     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24367 }
24368
24369 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24370     data : {},
24371     cb: false,
24372     displayField : false,
24373     tipField : false,
24374     
24375     
24376     defaultAutoCreate : {
24377         tag: 'div',
24378         cls: 'x-cbarray-item',
24379         cn : [ 
24380             { tag: 'div' },
24381             {
24382                 tag: 'img',
24383                 width:16,
24384                 height : 16,
24385                 src : Roo.BLANK_IMAGE_URL ,
24386                 align: 'center'
24387             }
24388         ]
24389         
24390     },
24391     
24392  
24393     onRender : function(ct, position)
24394     {
24395         Roo.form.Field.superclass.onRender.call(this, ct, position);
24396         
24397         if(!this.el){
24398             var cfg = this.getAutoCreate();
24399             this.el = ct.createChild(cfg, position);
24400         }
24401         
24402         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24403         
24404         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24405             this.cb.renderer(this.data) :
24406             String.format('{0}',this.data[this.displayField]);
24407         
24408             
24409         this.el.child('div').dom.setAttribute('qtip',
24410                         String.format('{0}',this.data[this.tipField])
24411         );
24412         
24413         this.el.child('img').on('click', this.remove, this);
24414         
24415     },
24416    
24417     remove : function()
24418     {
24419         if(this.cb.disabled){
24420             return;
24421         }
24422         
24423         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24424             this.cb.items.remove(this);
24425             this.el.child('img').un('click', this.remove, this);
24426             this.el.remove();
24427             this.cb.updateHiddenEl();
24428
24429             this.cb.fireEvent('remove', this.cb, this);
24430         }
24431         
24432     }
24433 });/*
24434  * Based on:
24435  * Ext JS Library 1.1.1
24436  * Copyright(c) 2006-2007, Ext JS, LLC.
24437  *
24438  * Originally Released Under LGPL - original licence link has changed is not relivant.
24439  *
24440  * Fork - LGPL
24441  * <script type="text/javascript">
24442  */
24443 /**
24444  * @class Roo.form.Checkbox
24445  * @extends Roo.form.Field
24446  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24447  * @constructor
24448  * Creates a new Checkbox
24449  * @param {Object} config Configuration options
24450  */
24451 Roo.form.Checkbox = function(config){
24452     Roo.form.Checkbox.superclass.constructor.call(this, config);
24453     this.addEvents({
24454         /**
24455          * @event check
24456          * Fires when the checkbox is checked or unchecked.
24457              * @param {Roo.form.Checkbox} this This checkbox
24458              * @param {Boolean} checked The new checked value
24459              */
24460         check : true
24461     });
24462 };
24463
24464 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24465     /**
24466      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24467      */
24468     focusClass : undefined,
24469     /**
24470      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24471      */
24472     fieldClass: "x-form-field",
24473     /**
24474      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24475      */
24476     checked: false,
24477     /**
24478      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24479      * {tag: "input", type: "checkbox", autocomplete: "off"})
24480      */
24481     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24482     /**
24483      * @cfg {String} boxLabel The text that appears beside the checkbox
24484      */
24485     boxLabel : "",
24486     /**
24487      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24488      */  
24489     inputValue : '1',
24490     /**
24491      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24492      */
24493      valueOff: '0', // value when not checked..
24494
24495     actionMode : 'viewEl', 
24496     //
24497     // private
24498     itemCls : 'x-menu-check-item x-form-item',
24499     groupClass : 'x-menu-group-item',
24500     inputType : 'hidden',
24501     
24502     
24503     inSetChecked: false, // check that we are not calling self...
24504     
24505     inputElement: false, // real input element?
24506     basedOn: false, // ????
24507     
24508     isFormField: true, // not sure where this is needed!!!!
24509
24510     onResize : function(){
24511         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24512         if(!this.boxLabel){
24513             this.el.alignTo(this.wrap, 'c-c');
24514         }
24515     },
24516
24517     initEvents : function(){
24518         Roo.form.Checkbox.superclass.initEvents.call(this);
24519         this.el.on("click", this.onClick,  this);
24520         this.el.on("change", this.onClick,  this);
24521     },
24522
24523
24524     getResizeEl : function(){
24525         return this.wrap;
24526     },
24527
24528     getPositionEl : function(){
24529         return this.wrap;
24530     },
24531
24532     // private
24533     onRender : function(ct, position){
24534         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24535         /*
24536         if(this.inputValue !== undefined){
24537             this.el.dom.value = this.inputValue;
24538         }
24539         */
24540         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24541         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24542         var viewEl = this.wrap.createChild({ 
24543             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24544         this.viewEl = viewEl;   
24545         this.wrap.on('click', this.onClick,  this); 
24546         
24547         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24548         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24549         
24550         
24551         
24552         if(this.boxLabel){
24553             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24554         //    viewEl.on('click', this.onClick,  this); 
24555         }
24556         //if(this.checked){
24557             this.setChecked(this.checked);
24558         //}else{
24559             //this.checked = this.el.dom;
24560         //}
24561
24562     },
24563
24564     // private
24565     initValue : Roo.emptyFn,
24566
24567     /**
24568      * Returns the checked state of the checkbox.
24569      * @return {Boolean} True if checked, else false
24570      */
24571     getValue : function(){
24572         if(this.el){
24573             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24574         }
24575         return this.valueOff;
24576         
24577     },
24578
24579         // private
24580     onClick : function(){ 
24581         if (this.disabled) {
24582             return;
24583         }
24584         this.setChecked(!this.checked);
24585
24586         //if(this.el.dom.checked != this.checked){
24587         //    this.setValue(this.el.dom.checked);
24588        // }
24589     },
24590
24591     /**
24592      * Sets the checked state of the checkbox.
24593      * On is always based on a string comparison between inputValue and the param.
24594      * @param {Boolean/String} value - the value to set 
24595      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24596      */
24597     setValue : function(v,suppressEvent){
24598         
24599         
24600         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24601         //if(this.el && this.el.dom){
24602         //    this.el.dom.checked = this.checked;
24603         //    this.el.dom.defaultChecked = this.checked;
24604         //}
24605         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24606         //this.fireEvent("check", this, this.checked);
24607     },
24608     // private..
24609     setChecked : function(state,suppressEvent)
24610     {
24611         if (this.inSetChecked) {
24612             this.checked = state;
24613             return;
24614         }
24615         
24616     
24617         if(this.wrap){
24618             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24619         }
24620         this.checked = state;
24621         if(suppressEvent !== true){
24622             this.fireEvent('check', this, state);
24623         }
24624         this.inSetChecked = true;
24625         this.el.dom.value = state ? this.inputValue : this.valueOff;
24626         this.inSetChecked = false;
24627         
24628     },
24629     // handle setting of hidden value by some other method!!?!?
24630     setFromHidden: function()
24631     {
24632         if(!this.el){
24633             return;
24634         }
24635         //console.log("SET FROM HIDDEN");
24636         //alert('setFrom hidden');
24637         this.setValue(this.el.dom.value);
24638     },
24639     
24640     onDestroy : function()
24641     {
24642         if(this.viewEl){
24643             Roo.get(this.viewEl).remove();
24644         }
24645          
24646         Roo.form.Checkbox.superclass.onDestroy.call(this);
24647     },
24648     
24649     setBoxLabel : function(str)
24650     {
24651         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
24652     }
24653
24654 });/*
24655  * Based on:
24656  * Ext JS Library 1.1.1
24657  * Copyright(c) 2006-2007, Ext JS, LLC.
24658  *
24659  * Originally Released Under LGPL - original licence link has changed is not relivant.
24660  *
24661  * Fork - LGPL
24662  * <script type="text/javascript">
24663  */
24664  
24665 /**
24666  * @class Roo.form.Radio
24667  * @extends Roo.form.Checkbox
24668  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24669  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24670  * @constructor
24671  * Creates a new Radio
24672  * @param {Object} config Configuration options
24673  */
24674 Roo.form.Radio = function(){
24675     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24676 };
24677 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24678     inputType: 'radio',
24679
24680     /**
24681      * If this radio is part of a group, it will return the selected value
24682      * @return {String}
24683      */
24684     getGroupValue : function(){
24685         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24686     },
24687     
24688     
24689     onRender : function(ct, position){
24690         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24691         
24692         if(this.inputValue !== undefined){
24693             this.el.dom.value = this.inputValue;
24694         }
24695          
24696         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24697         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24698         //var viewEl = this.wrap.createChild({ 
24699         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24700         //this.viewEl = viewEl;   
24701         //this.wrap.on('click', this.onClick,  this); 
24702         
24703         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24704         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24705         
24706         
24707         
24708         if(this.boxLabel){
24709             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24710         //    viewEl.on('click', this.onClick,  this); 
24711         }
24712          if(this.checked){
24713             this.el.dom.checked =   'checked' ;
24714         }
24715          
24716     } 
24717     
24718     
24719 });//<script type="text/javascript">
24720
24721 /*
24722  * Based  Ext JS Library 1.1.1
24723  * Copyright(c) 2006-2007, Ext JS, LLC.
24724  * LGPL
24725  *
24726  */
24727  
24728 /**
24729  * @class Roo.HtmlEditorCore
24730  * @extends Roo.Component
24731  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24732  *
24733  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24734  */
24735
24736 Roo.HtmlEditorCore = function(config){
24737     
24738     
24739     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24740     
24741     
24742     this.addEvents({
24743         /**
24744          * @event initialize
24745          * Fires when the editor is fully initialized (including the iframe)
24746          * @param {Roo.HtmlEditorCore} this
24747          */
24748         initialize: true,
24749         /**
24750          * @event activate
24751          * Fires when the editor is first receives the focus. Any insertion must wait
24752          * until after this event.
24753          * @param {Roo.HtmlEditorCore} this
24754          */
24755         activate: true,
24756          /**
24757          * @event beforesync
24758          * Fires before the textarea is updated with content from the editor iframe. Return false
24759          * to cancel the sync.
24760          * @param {Roo.HtmlEditorCore} this
24761          * @param {String} html
24762          */
24763         beforesync: true,
24764          /**
24765          * @event beforepush
24766          * Fires before the iframe editor is updated with content from the textarea. Return false
24767          * to cancel the push.
24768          * @param {Roo.HtmlEditorCore} this
24769          * @param {String} html
24770          */
24771         beforepush: true,
24772          /**
24773          * @event sync
24774          * Fires when the textarea is updated with content from the editor iframe.
24775          * @param {Roo.HtmlEditorCore} this
24776          * @param {String} html
24777          */
24778         sync: true,
24779          /**
24780          * @event push
24781          * Fires when the iframe editor is updated with content from the textarea.
24782          * @param {Roo.HtmlEditorCore} this
24783          * @param {String} html
24784          */
24785         push: true,
24786         
24787         /**
24788          * @event editorevent
24789          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24790          * @param {Roo.HtmlEditorCore} this
24791          */
24792         editorevent: true
24793         
24794     });
24795     
24796     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24797     
24798     // defaults : white / black...
24799     this.applyBlacklists();
24800     
24801     
24802     
24803 };
24804
24805
24806 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24807
24808
24809      /**
24810      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24811      */
24812     
24813     owner : false,
24814     
24815      /**
24816      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24817      *                        Roo.resizable.
24818      */
24819     resizable : false,
24820      /**
24821      * @cfg {Number} height (in pixels)
24822      */   
24823     height: 300,
24824    /**
24825      * @cfg {Number} width (in pixels)
24826      */   
24827     width: 500,
24828     
24829     /**
24830      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24831      * 
24832      */
24833     stylesheets: false,
24834     
24835     // id of frame..
24836     frameId: false,
24837     
24838     // private properties
24839     validationEvent : false,
24840     deferHeight: true,
24841     initialized : false,
24842     activated : false,
24843     sourceEditMode : false,
24844     onFocus : Roo.emptyFn,
24845     iframePad:3,
24846     hideMode:'offsets',
24847     
24848     clearUp: true,
24849     
24850     // blacklist + whitelisted elements..
24851     black: false,
24852     white: false,
24853      
24854     
24855
24856     /**
24857      * Protected method that will not generally be called directly. It
24858      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24859      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24860      */
24861     getDocMarkup : function(){
24862         // body styles..
24863         var st = '';
24864         
24865         // inherit styels from page...?? 
24866         if (this.stylesheets === false) {
24867             
24868             Roo.get(document.head).select('style').each(function(node) {
24869                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24870             });
24871             
24872             Roo.get(document.head).select('link').each(function(node) { 
24873                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24874             });
24875             
24876         } else if (!this.stylesheets.length) {
24877                 // simple..
24878                 st = '<style type="text/css">' +
24879                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24880                    '</style>';
24881         } else { 
24882             
24883         }
24884         
24885         st +=  '<style type="text/css">' +
24886             'IMG { cursor: pointer } ' +
24887         '</style>';
24888
24889         
24890         return '<html><head>' + st  +
24891             //<style type="text/css">' +
24892             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24893             //'</style>' +
24894             ' </head><body class="roo-htmleditor-body"></body></html>';
24895     },
24896
24897     // private
24898     onRender : function(ct, position)
24899     {
24900         var _t = this;
24901         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24902         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24903         
24904         
24905         this.el.dom.style.border = '0 none';
24906         this.el.dom.setAttribute('tabIndex', -1);
24907         this.el.addClass('x-hidden hide');
24908         
24909         
24910         
24911         if(Roo.isIE){ // fix IE 1px bogus margin
24912             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24913         }
24914        
24915         
24916         this.frameId = Roo.id();
24917         
24918          
24919         
24920         var iframe = this.owner.wrap.createChild({
24921             tag: 'iframe',
24922             cls: 'form-control', // bootstrap..
24923             id: this.frameId,
24924             name: this.frameId,
24925             frameBorder : 'no',
24926             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24927         }, this.el
24928         );
24929         
24930         
24931         this.iframe = iframe.dom;
24932
24933          this.assignDocWin();
24934         
24935         this.doc.designMode = 'on';
24936        
24937         this.doc.open();
24938         this.doc.write(this.getDocMarkup());
24939         this.doc.close();
24940
24941         
24942         var task = { // must defer to wait for browser to be ready
24943             run : function(){
24944                 //console.log("run task?" + this.doc.readyState);
24945                 this.assignDocWin();
24946                 if(this.doc.body || this.doc.readyState == 'complete'){
24947                     try {
24948                         this.doc.designMode="on";
24949                     } catch (e) {
24950                         return;
24951                     }
24952                     Roo.TaskMgr.stop(task);
24953                     this.initEditor.defer(10, this);
24954                 }
24955             },
24956             interval : 10,
24957             duration: 10000,
24958             scope: this
24959         };
24960         Roo.TaskMgr.start(task);
24961
24962     },
24963
24964     // private
24965     onResize : function(w, h)
24966     {
24967          Roo.log('resize: ' +w + ',' + h );
24968         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24969         if(!this.iframe){
24970             return;
24971         }
24972         if(typeof w == 'number'){
24973             
24974             this.iframe.style.width = w + 'px';
24975         }
24976         if(typeof h == 'number'){
24977             
24978             this.iframe.style.height = h + 'px';
24979             if(this.doc){
24980                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24981             }
24982         }
24983         
24984     },
24985
24986     /**
24987      * Toggles the editor between standard and source edit mode.
24988      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24989      */
24990     toggleSourceEdit : function(sourceEditMode){
24991         
24992         this.sourceEditMode = sourceEditMode === true;
24993         
24994         if(this.sourceEditMode){
24995  
24996             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24997             
24998         }else{
24999             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25000             //this.iframe.className = '';
25001             this.deferFocus();
25002         }
25003         //this.setSize(this.owner.wrap.getSize());
25004         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25005     },
25006
25007     
25008   
25009
25010     /**
25011      * Protected method that will not generally be called directly. If you need/want
25012      * custom HTML cleanup, this is the method you should override.
25013      * @param {String} html The HTML to be cleaned
25014      * return {String} The cleaned HTML
25015      */
25016     cleanHtml : function(html){
25017         html = String(html);
25018         if(html.length > 5){
25019             if(Roo.isSafari){ // strip safari nonsense
25020                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25021             }
25022         }
25023         if(html == '&nbsp;'){
25024             html = '';
25025         }
25026         return html;
25027     },
25028
25029     /**
25030      * HTML Editor -> Textarea
25031      * Protected method that will not generally be called directly. Syncs the contents
25032      * of the editor iframe with the textarea.
25033      */
25034     syncValue : function(){
25035         if(this.initialized){
25036             var bd = (this.doc.body || this.doc.documentElement);
25037             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25038             var html = bd.innerHTML;
25039             if(Roo.isSafari){
25040                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25041                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25042                 if(m && m[1]){
25043                     html = '<div style="'+m[0]+'">' + html + '</div>';
25044                 }
25045             }
25046             html = this.cleanHtml(html);
25047             // fix up the special chars.. normaly like back quotes in word...
25048             // however we do not want to do this with chinese..
25049             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25050                 var cc = b.charCodeAt();
25051                 if (
25052                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25053                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25054                     (cc >= 0xf900 && cc < 0xfb00 )
25055                 ) {
25056                         return b;
25057                 }
25058                 return "&#"+cc+";" 
25059             });
25060             if(this.owner.fireEvent('beforesync', this, html) !== false){
25061                 this.el.dom.value = html;
25062                 this.owner.fireEvent('sync', this, html);
25063             }
25064         }
25065     },
25066
25067     /**
25068      * Protected method that will not generally be called directly. Pushes the value of the textarea
25069      * into the iframe editor.
25070      */
25071     pushValue : function(){
25072         if(this.initialized){
25073             var v = this.el.dom.value.trim();
25074             
25075 //            if(v.length < 1){
25076 //                v = '&#160;';
25077 //            }
25078             
25079             if(this.owner.fireEvent('beforepush', this, v) !== false){
25080                 var d = (this.doc.body || this.doc.documentElement);
25081                 d.innerHTML = v;
25082                 this.cleanUpPaste();
25083                 this.el.dom.value = d.innerHTML;
25084                 this.owner.fireEvent('push', this, v);
25085             }
25086         }
25087     },
25088
25089     // private
25090     deferFocus : function(){
25091         this.focus.defer(10, this);
25092     },
25093
25094     // doc'ed in Field
25095     focus : function(){
25096         if(this.win && !this.sourceEditMode){
25097             this.win.focus();
25098         }else{
25099             this.el.focus();
25100         }
25101     },
25102     
25103     assignDocWin: function()
25104     {
25105         var iframe = this.iframe;
25106         
25107          if(Roo.isIE){
25108             this.doc = iframe.contentWindow.document;
25109             this.win = iframe.contentWindow;
25110         } else {
25111 //            if (!Roo.get(this.frameId)) {
25112 //                return;
25113 //            }
25114 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25115 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25116             
25117             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25118                 return;
25119             }
25120             
25121             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25122             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25123         }
25124     },
25125     
25126     // private
25127     initEditor : function(){
25128         //console.log("INIT EDITOR");
25129         this.assignDocWin();
25130         
25131         
25132         
25133         this.doc.designMode="on";
25134         this.doc.open();
25135         this.doc.write(this.getDocMarkup());
25136         this.doc.close();
25137         
25138         var dbody = (this.doc.body || this.doc.documentElement);
25139         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25140         // this copies styles from the containing element into thsi one..
25141         // not sure why we need all of this..
25142         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25143         
25144         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25145         //ss['background-attachment'] = 'fixed'; // w3c
25146         dbody.bgProperties = 'fixed'; // ie
25147         //Roo.DomHelper.applyStyles(dbody, ss);
25148         Roo.EventManager.on(this.doc, {
25149             //'mousedown': this.onEditorEvent,
25150             'mouseup': this.onEditorEvent,
25151             'dblclick': this.onEditorEvent,
25152             'click': this.onEditorEvent,
25153             'keyup': this.onEditorEvent,
25154             buffer:100,
25155             scope: this
25156         });
25157         if(Roo.isGecko){
25158             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25159         }
25160         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25161             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25162         }
25163         this.initialized = true;
25164
25165         this.owner.fireEvent('initialize', this);
25166         this.pushValue();
25167     },
25168
25169     // private
25170     onDestroy : function(){
25171         
25172         
25173         
25174         if(this.rendered){
25175             
25176             //for (var i =0; i < this.toolbars.length;i++) {
25177             //    // fixme - ask toolbars for heights?
25178             //    this.toolbars[i].onDestroy();
25179            // }
25180             
25181             //this.wrap.dom.innerHTML = '';
25182             //this.wrap.remove();
25183         }
25184     },
25185
25186     // private
25187     onFirstFocus : function(){
25188         
25189         this.assignDocWin();
25190         
25191         
25192         this.activated = true;
25193          
25194     
25195         if(Roo.isGecko){ // prevent silly gecko errors
25196             this.win.focus();
25197             var s = this.win.getSelection();
25198             if(!s.focusNode || s.focusNode.nodeType != 3){
25199                 var r = s.getRangeAt(0);
25200                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25201                 r.collapse(true);
25202                 this.deferFocus();
25203             }
25204             try{
25205                 this.execCmd('useCSS', true);
25206                 this.execCmd('styleWithCSS', false);
25207             }catch(e){}
25208         }
25209         this.owner.fireEvent('activate', this);
25210     },
25211
25212     // private
25213     adjustFont: function(btn){
25214         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25215         //if(Roo.isSafari){ // safari
25216         //    adjust *= 2;
25217        // }
25218         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25219         if(Roo.isSafari){ // safari
25220             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25221             v =  (v < 10) ? 10 : v;
25222             v =  (v > 48) ? 48 : v;
25223             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25224             
25225         }
25226         
25227         
25228         v = Math.max(1, v+adjust);
25229         
25230         this.execCmd('FontSize', v  );
25231     },
25232
25233     onEditorEvent : function(e)
25234     {
25235         this.owner.fireEvent('editorevent', this, e);
25236       //  this.updateToolbar();
25237         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25238     },
25239
25240     insertTag : function(tg)
25241     {
25242         // could be a bit smarter... -> wrap the current selected tRoo..
25243         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25244             
25245             range = this.createRange(this.getSelection());
25246             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25247             wrappingNode.appendChild(range.extractContents());
25248             range.insertNode(wrappingNode);
25249
25250             return;
25251             
25252             
25253             
25254         }
25255         this.execCmd("formatblock",   tg);
25256         
25257     },
25258     
25259     insertText : function(txt)
25260     {
25261         
25262         
25263         var range = this.createRange();
25264         range.deleteContents();
25265                //alert(Sender.getAttribute('label'));
25266                
25267         range.insertNode(this.doc.createTextNode(txt));
25268     } ,
25269     
25270      
25271
25272     /**
25273      * Executes a Midas editor command on the editor document and performs necessary focus and
25274      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25275      * @param {String} cmd The Midas command
25276      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25277      */
25278     relayCmd : function(cmd, value){
25279         this.win.focus();
25280         this.execCmd(cmd, value);
25281         this.owner.fireEvent('editorevent', this);
25282         //this.updateToolbar();
25283         this.owner.deferFocus();
25284     },
25285
25286     /**
25287      * Executes a Midas editor command directly on the editor document.
25288      * For visual commands, you should use {@link #relayCmd} instead.
25289      * <b>This should only be called after the editor is initialized.</b>
25290      * @param {String} cmd The Midas command
25291      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25292      */
25293     execCmd : function(cmd, value){
25294         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25295         this.syncValue();
25296     },
25297  
25298  
25299    
25300     /**
25301      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25302      * to insert tRoo.
25303      * @param {String} text | dom node.. 
25304      */
25305     insertAtCursor : function(text)
25306     {
25307         
25308         
25309         
25310         if(!this.activated){
25311             return;
25312         }
25313         /*
25314         if(Roo.isIE){
25315             this.win.focus();
25316             var r = this.doc.selection.createRange();
25317             if(r){
25318                 r.collapse(true);
25319                 r.pasteHTML(text);
25320                 this.syncValue();
25321                 this.deferFocus();
25322             
25323             }
25324             return;
25325         }
25326         */
25327         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25328             this.win.focus();
25329             
25330             
25331             // from jquery ui (MIT licenced)
25332             var range, node;
25333             var win = this.win;
25334             
25335             if (win.getSelection && win.getSelection().getRangeAt) {
25336                 range = win.getSelection().getRangeAt(0);
25337                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25338                 range.insertNode(node);
25339             } else if (win.document.selection && win.document.selection.createRange) {
25340                 // no firefox support
25341                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25342                 win.document.selection.createRange().pasteHTML(txt);
25343             } else {
25344                 // no firefox support
25345                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25346                 this.execCmd('InsertHTML', txt);
25347             } 
25348             
25349             this.syncValue();
25350             
25351             this.deferFocus();
25352         }
25353     },
25354  // private
25355     mozKeyPress : function(e){
25356         if(e.ctrlKey){
25357             var c = e.getCharCode(), cmd;
25358           
25359             if(c > 0){
25360                 c = String.fromCharCode(c).toLowerCase();
25361                 switch(c){
25362                     case 'b':
25363                         cmd = 'bold';
25364                         break;
25365                     case 'i':
25366                         cmd = 'italic';
25367                         break;
25368                     
25369                     case 'u':
25370                         cmd = 'underline';
25371                         break;
25372                     
25373                     case 'v':
25374                         this.cleanUpPaste.defer(100, this);
25375                         return;
25376                         
25377                 }
25378                 if(cmd){
25379                     this.win.focus();
25380                     this.execCmd(cmd);
25381                     this.deferFocus();
25382                     e.preventDefault();
25383                 }
25384                 
25385             }
25386         }
25387     },
25388
25389     // private
25390     fixKeys : function(){ // load time branching for fastest keydown performance
25391         if(Roo.isIE){
25392             return function(e){
25393                 var k = e.getKey(), r;
25394                 if(k == e.TAB){
25395                     e.stopEvent();
25396                     r = this.doc.selection.createRange();
25397                     if(r){
25398                         r.collapse(true);
25399                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25400                         this.deferFocus();
25401                     }
25402                     return;
25403                 }
25404                 
25405                 if(k == e.ENTER){
25406                     r = this.doc.selection.createRange();
25407                     if(r){
25408                         var target = r.parentElement();
25409                         if(!target || target.tagName.toLowerCase() != 'li'){
25410                             e.stopEvent();
25411                             r.pasteHTML('<br />');
25412                             r.collapse(false);
25413                             r.select();
25414                         }
25415                     }
25416                 }
25417                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25418                     this.cleanUpPaste.defer(100, this);
25419                     return;
25420                 }
25421                 
25422                 
25423             };
25424         }else if(Roo.isOpera){
25425             return function(e){
25426                 var k = e.getKey();
25427                 if(k == e.TAB){
25428                     e.stopEvent();
25429                     this.win.focus();
25430                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25431                     this.deferFocus();
25432                 }
25433                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25434                     this.cleanUpPaste.defer(100, this);
25435                     return;
25436                 }
25437                 
25438             };
25439         }else if(Roo.isSafari){
25440             return function(e){
25441                 var k = e.getKey();
25442                 
25443                 if(k == e.TAB){
25444                     e.stopEvent();
25445                     this.execCmd('InsertText','\t');
25446                     this.deferFocus();
25447                     return;
25448                 }
25449                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25450                     this.cleanUpPaste.defer(100, this);
25451                     return;
25452                 }
25453                 
25454              };
25455         }
25456     }(),
25457     
25458     getAllAncestors: function()
25459     {
25460         var p = this.getSelectedNode();
25461         var a = [];
25462         if (!p) {
25463             a.push(p); // push blank onto stack..
25464             p = this.getParentElement();
25465         }
25466         
25467         
25468         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25469             a.push(p);
25470             p = p.parentNode;
25471         }
25472         a.push(this.doc.body);
25473         return a;
25474     },
25475     lastSel : false,
25476     lastSelNode : false,
25477     
25478     
25479     getSelection : function() 
25480     {
25481         this.assignDocWin();
25482         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25483     },
25484     
25485     getSelectedNode: function() 
25486     {
25487         // this may only work on Gecko!!!
25488         
25489         // should we cache this!!!!
25490         
25491         
25492         
25493          
25494         var range = this.createRange(this.getSelection()).cloneRange();
25495         
25496         if (Roo.isIE) {
25497             var parent = range.parentElement();
25498             while (true) {
25499                 var testRange = range.duplicate();
25500                 testRange.moveToElementText(parent);
25501                 if (testRange.inRange(range)) {
25502                     break;
25503                 }
25504                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25505                     break;
25506                 }
25507                 parent = parent.parentElement;
25508             }
25509             return parent;
25510         }
25511         
25512         // is ancestor a text element.
25513         var ac =  range.commonAncestorContainer;
25514         if (ac.nodeType == 3) {
25515             ac = ac.parentNode;
25516         }
25517         
25518         var ar = ac.childNodes;
25519          
25520         var nodes = [];
25521         var other_nodes = [];
25522         var has_other_nodes = false;
25523         for (var i=0;i<ar.length;i++) {
25524             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25525                 continue;
25526             }
25527             // fullly contained node.
25528             
25529             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25530                 nodes.push(ar[i]);
25531                 continue;
25532             }
25533             
25534             // probably selected..
25535             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25536                 other_nodes.push(ar[i]);
25537                 continue;
25538             }
25539             // outer..
25540             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25541                 continue;
25542             }
25543             
25544             
25545             has_other_nodes = true;
25546         }
25547         if (!nodes.length && other_nodes.length) {
25548             nodes= other_nodes;
25549         }
25550         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25551             return false;
25552         }
25553         
25554         return nodes[0];
25555     },
25556     createRange: function(sel)
25557     {
25558         // this has strange effects when using with 
25559         // top toolbar - not sure if it's a great idea.
25560         //this.editor.contentWindow.focus();
25561         if (typeof sel != "undefined") {
25562             try {
25563                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25564             } catch(e) {
25565                 return this.doc.createRange();
25566             }
25567         } else {
25568             return this.doc.createRange();
25569         }
25570     },
25571     getParentElement: function()
25572     {
25573         
25574         this.assignDocWin();
25575         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25576         
25577         var range = this.createRange(sel);
25578          
25579         try {
25580             var p = range.commonAncestorContainer;
25581             while (p.nodeType == 3) { // text node
25582                 p = p.parentNode;
25583             }
25584             return p;
25585         } catch (e) {
25586             return null;
25587         }
25588     
25589     },
25590     /***
25591      *
25592      * Range intersection.. the hard stuff...
25593      *  '-1' = before
25594      *  '0' = hits..
25595      *  '1' = after.
25596      *         [ -- selected range --- ]
25597      *   [fail]                        [fail]
25598      *
25599      *    basically..
25600      *      if end is before start or  hits it. fail.
25601      *      if start is after end or hits it fail.
25602      *
25603      *   if either hits (but other is outside. - then it's not 
25604      *   
25605      *    
25606      **/
25607     
25608     
25609     // @see http://www.thismuchiknow.co.uk/?p=64.
25610     rangeIntersectsNode : function(range, node)
25611     {
25612         var nodeRange = node.ownerDocument.createRange();
25613         try {
25614             nodeRange.selectNode(node);
25615         } catch (e) {
25616             nodeRange.selectNodeContents(node);
25617         }
25618     
25619         var rangeStartRange = range.cloneRange();
25620         rangeStartRange.collapse(true);
25621     
25622         var rangeEndRange = range.cloneRange();
25623         rangeEndRange.collapse(false);
25624     
25625         var nodeStartRange = nodeRange.cloneRange();
25626         nodeStartRange.collapse(true);
25627     
25628         var nodeEndRange = nodeRange.cloneRange();
25629         nodeEndRange.collapse(false);
25630     
25631         return rangeStartRange.compareBoundaryPoints(
25632                  Range.START_TO_START, nodeEndRange) == -1 &&
25633                rangeEndRange.compareBoundaryPoints(
25634                  Range.START_TO_START, nodeStartRange) == 1;
25635         
25636          
25637     },
25638     rangeCompareNode : function(range, node)
25639     {
25640         var nodeRange = node.ownerDocument.createRange();
25641         try {
25642             nodeRange.selectNode(node);
25643         } catch (e) {
25644             nodeRange.selectNodeContents(node);
25645         }
25646         
25647         
25648         range.collapse(true);
25649     
25650         nodeRange.collapse(true);
25651      
25652         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25653         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25654          
25655         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25656         
25657         var nodeIsBefore   =  ss == 1;
25658         var nodeIsAfter    = ee == -1;
25659         
25660         if (nodeIsBefore && nodeIsAfter) {
25661             return 0; // outer
25662         }
25663         if (!nodeIsBefore && nodeIsAfter) {
25664             return 1; //right trailed.
25665         }
25666         
25667         if (nodeIsBefore && !nodeIsAfter) {
25668             return 2;  // left trailed.
25669         }
25670         // fully contined.
25671         return 3;
25672     },
25673
25674     // private? - in a new class?
25675     cleanUpPaste :  function()
25676     {
25677         // cleans up the whole document..
25678         Roo.log('cleanuppaste');
25679         
25680         this.cleanUpChildren(this.doc.body);
25681         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25682         if (clean != this.doc.body.innerHTML) {
25683             this.doc.body.innerHTML = clean;
25684         }
25685         
25686     },
25687     
25688     cleanWordChars : function(input) {// change the chars to hex code
25689         var he = Roo.HtmlEditorCore;
25690         
25691         var output = input;
25692         Roo.each(he.swapCodes, function(sw) { 
25693             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25694             
25695             output = output.replace(swapper, sw[1]);
25696         });
25697         
25698         return output;
25699     },
25700     
25701     
25702     cleanUpChildren : function (n)
25703     {
25704         if (!n.childNodes.length) {
25705             return;
25706         }
25707         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25708            this.cleanUpChild(n.childNodes[i]);
25709         }
25710     },
25711     
25712     
25713         
25714     
25715     cleanUpChild : function (node)
25716     {
25717         var ed = this;
25718         //console.log(node);
25719         if (node.nodeName == "#text") {
25720             // clean up silly Windows -- stuff?
25721             return; 
25722         }
25723         if (node.nodeName == "#comment") {
25724             node.parentNode.removeChild(node);
25725             // clean up silly Windows -- stuff?
25726             return; 
25727         }
25728         var lcname = node.tagName.toLowerCase();
25729         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25730         // whitelist of tags..
25731         
25732         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25733             // remove node.
25734             node.parentNode.removeChild(node);
25735             return;
25736             
25737         }
25738         
25739         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25740         
25741         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25742         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25743         
25744         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25745         //    remove_keep_children = true;
25746         //}
25747         
25748         if (remove_keep_children) {
25749             this.cleanUpChildren(node);
25750             // inserts everything just before this node...
25751             while (node.childNodes.length) {
25752                 var cn = node.childNodes[0];
25753                 node.removeChild(cn);
25754                 node.parentNode.insertBefore(cn, node);
25755             }
25756             node.parentNode.removeChild(node);
25757             return;
25758         }
25759         
25760         if (!node.attributes || !node.attributes.length) {
25761             this.cleanUpChildren(node);
25762             return;
25763         }
25764         
25765         function cleanAttr(n,v)
25766         {
25767             
25768             if (v.match(/^\./) || v.match(/^\//)) {
25769                 return;
25770             }
25771             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25772                 return;
25773             }
25774             if (v.match(/^#/)) {
25775                 return;
25776             }
25777 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25778             node.removeAttribute(n);
25779             
25780         }
25781         
25782         var cwhite = this.cwhite;
25783         var cblack = this.cblack;
25784             
25785         function cleanStyle(n,v)
25786         {
25787             if (v.match(/expression/)) { //XSS?? should we even bother..
25788                 node.removeAttribute(n);
25789                 return;
25790             }
25791             
25792             var parts = v.split(/;/);
25793             var clean = [];
25794             
25795             Roo.each(parts, function(p) {
25796                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25797                 if (!p.length) {
25798                     return true;
25799                 }
25800                 var l = p.split(':').shift().replace(/\s+/g,'');
25801                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25802                 
25803                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25804 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25805                     //node.removeAttribute(n);
25806                     return true;
25807                 }
25808                 //Roo.log()
25809                 // only allow 'c whitelisted system attributes'
25810                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25811 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25812                     //node.removeAttribute(n);
25813                     return true;
25814                 }
25815                 
25816                 
25817                  
25818                 
25819                 clean.push(p);
25820                 return true;
25821             });
25822             if (clean.length) { 
25823                 node.setAttribute(n, clean.join(';'));
25824             } else {
25825                 node.removeAttribute(n);
25826             }
25827             
25828         }
25829         
25830         
25831         for (var i = node.attributes.length-1; i > -1 ; i--) {
25832             var a = node.attributes[i];
25833             //console.log(a);
25834             
25835             if (a.name.toLowerCase().substr(0,2)=='on')  {
25836                 node.removeAttribute(a.name);
25837                 continue;
25838             }
25839             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25840                 node.removeAttribute(a.name);
25841                 continue;
25842             }
25843             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25844                 cleanAttr(a.name,a.value); // fixme..
25845                 continue;
25846             }
25847             if (a.name == 'style') {
25848                 cleanStyle(a.name,a.value);
25849                 continue;
25850             }
25851             /// clean up MS crap..
25852             // tecnically this should be a list of valid class'es..
25853             
25854             
25855             if (a.name == 'class') {
25856                 if (a.value.match(/^Mso/)) {
25857                     node.className = '';
25858                 }
25859                 
25860                 if (a.value.match(/body/)) {
25861                     node.className = '';
25862                 }
25863                 continue;
25864             }
25865             
25866             // style cleanup!?
25867             // class cleanup?
25868             
25869         }
25870         
25871         
25872         this.cleanUpChildren(node);
25873         
25874         
25875     },
25876     
25877     /**
25878      * Clean up MS wordisms...
25879      */
25880     cleanWord : function(node)
25881     {
25882         
25883         
25884         if (!node) {
25885             this.cleanWord(this.doc.body);
25886             return;
25887         }
25888         if (node.nodeName == "#text") {
25889             // clean up silly Windows -- stuff?
25890             return; 
25891         }
25892         if (node.nodeName == "#comment") {
25893             node.parentNode.removeChild(node);
25894             // clean up silly Windows -- stuff?
25895             return; 
25896         }
25897         
25898         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25899             node.parentNode.removeChild(node);
25900             return;
25901         }
25902         
25903         // remove - but keep children..
25904         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25905             while (node.childNodes.length) {
25906                 var cn = node.childNodes[0];
25907                 node.removeChild(cn);
25908                 node.parentNode.insertBefore(cn, node);
25909             }
25910             node.parentNode.removeChild(node);
25911             this.iterateChildren(node, this.cleanWord);
25912             return;
25913         }
25914         // clean styles
25915         if (node.className.length) {
25916             
25917             var cn = node.className.split(/\W+/);
25918             var cna = [];
25919             Roo.each(cn, function(cls) {
25920                 if (cls.match(/Mso[a-zA-Z]+/)) {
25921                     return;
25922                 }
25923                 cna.push(cls);
25924             });
25925             node.className = cna.length ? cna.join(' ') : '';
25926             if (!cna.length) {
25927                 node.removeAttribute("class");
25928             }
25929         }
25930         
25931         if (node.hasAttribute("lang")) {
25932             node.removeAttribute("lang");
25933         }
25934         
25935         if (node.hasAttribute("style")) {
25936             
25937             var styles = node.getAttribute("style").split(";");
25938             var nstyle = [];
25939             Roo.each(styles, function(s) {
25940                 if (!s.match(/:/)) {
25941                     return;
25942                 }
25943                 var kv = s.split(":");
25944                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25945                     return;
25946                 }
25947                 // what ever is left... we allow.
25948                 nstyle.push(s);
25949             });
25950             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25951             if (!nstyle.length) {
25952                 node.removeAttribute('style');
25953             }
25954         }
25955         this.iterateChildren(node, this.cleanWord);
25956         
25957         
25958         
25959     },
25960     /**
25961      * iterateChildren of a Node, calling fn each time, using this as the scole..
25962      * @param {DomNode} node node to iterate children of.
25963      * @param {Function} fn method of this class to call on each item.
25964      */
25965     iterateChildren : function(node, fn)
25966     {
25967         if (!node.childNodes.length) {
25968                 return;
25969         }
25970         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25971            fn.call(this, node.childNodes[i])
25972         }
25973     },
25974     
25975     
25976     /**
25977      * cleanTableWidths.
25978      *
25979      * Quite often pasting from word etc.. results in tables with column and widths.
25980      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25981      *
25982      */
25983     cleanTableWidths : function(node)
25984     {
25985          
25986          
25987         if (!node) {
25988             this.cleanTableWidths(this.doc.body);
25989             return;
25990         }
25991         
25992         // ignore list...
25993         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25994             return; 
25995         }
25996         Roo.log(node.tagName);
25997         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25998             this.iterateChildren(node, this.cleanTableWidths);
25999             return;
26000         }
26001         if (node.hasAttribute('width')) {
26002             node.removeAttribute('width');
26003         }
26004         
26005          
26006         if (node.hasAttribute("style")) {
26007             // pretty basic...
26008             
26009             var styles = node.getAttribute("style").split(";");
26010             var nstyle = [];
26011             Roo.each(styles, function(s) {
26012                 if (!s.match(/:/)) {
26013                     return;
26014                 }
26015                 var kv = s.split(":");
26016                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26017                     return;
26018                 }
26019                 // what ever is left... we allow.
26020                 nstyle.push(s);
26021             });
26022             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26023             if (!nstyle.length) {
26024                 node.removeAttribute('style');
26025             }
26026         }
26027         
26028         this.iterateChildren(node, this.cleanTableWidths);
26029         
26030         
26031     },
26032     
26033     
26034     
26035     
26036     domToHTML : function(currentElement, depth, nopadtext) {
26037         
26038         depth = depth || 0;
26039         nopadtext = nopadtext || false;
26040     
26041         if (!currentElement) {
26042             return this.domToHTML(this.doc.body);
26043         }
26044         
26045         //Roo.log(currentElement);
26046         var j;
26047         var allText = false;
26048         var nodeName = currentElement.nodeName;
26049         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26050         
26051         if  (nodeName == '#text') {
26052             
26053             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26054         }
26055         
26056         
26057         var ret = '';
26058         if (nodeName != 'BODY') {
26059              
26060             var i = 0;
26061             // Prints the node tagName, such as <A>, <IMG>, etc
26062             if (tagName) {
26063                 var attr = [];
26064                 for(i = 0; i < currentElement.attributes.length;i++) {
26065                     // quoting?
26066                     var aname = currentElement.attributes.item(i).name;
26067                     if (!currentElement.attributes.item(i).value.length) {
26068                         continue;
26069                     }
26070                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26071                 }
26072                 
26073                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26074             } 
26075             else {
26076                 
26077                 // eack
26078             }
26079         } else {
26080             tagName = false;
26081         }
26082         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26083             return ret;
26084         }
26085         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26086             nopadtext = true;
26087         }
26088         
26089         
26090         // Traverse the tree
26091         i = 0;
26092         var currentElementChild = currentElement.childNodes.item(i);
26093         var allText = true;
26094         var innerHTML  = '';
26095         lastnode = '';
26096         while (currentElementChild) {
26097             // Formatting code (indent the tree so it looks nice on the screen)
26098             var nopad = nopadtext;
26099             if (lastnode == 'SPAN') {
26100                 nopad  = true;
26101             }
26102             // text
26103             if  (currentElementChild.nodeName == '#text') {
26104                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26105                 toadd = nopadtext ? toadd : toadd.trim();
26106                 if (!nopad && toadd.length > 80) {
26107                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26108                 }
26109                 innerHTML  += toadd;
26110                 
26111                 i++;
26112                 currentElementChild = currentElement.childNodes.item(i);
26113                 lastNode = '';
26114                 continue;
26115             }
26116             allText = false;
26117             
26118             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26119                 
26120             // Recursively traverse the tree structure of the child node
26121             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26122             lastnode = currentElementChild.nodeName;
26123             i++;
26124             currentElementChild=currentElement.childNodes.item(i);
26125         }
26126         
26127         ret += innerHTML;
26128         
26129         if (!allText) {
26130                 // The remaining code is mostly for formatting the tree
26131             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26132         }
26133         
26134         
26135         if (tagName) {
26136             ret+= "</"+tagName+">";
26137         }
26138         return ret;
26139         
26140     },
26141         
26142     applyBlacklists : function()
26143     {
26144         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26145         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26146         
26147         this.white = [];
26148         this.black = [];
26149         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26150             if (b.indexOf(tag) > -1) {
26151                 return;
26152             }
26153             this.white.push(tag);
26154             
26155         }, this);
26156         
26157         Roo.each(w, function(tag) {
26158             if (b.indexOf(tag) > -1) {
26159                 return;
26160             }
26161             if (this.white.indexOf(tag) > -1) {
26162                 return;
26163             }
26164             this.white.push(tag);
26165             
26166         }, this);
26167         
26168         
26169         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26170             if (w.indexOf(tag) > -1) {
26171                 return;
26172             }
26173             this.black.push(tag);
26174             
26175         }, this);
26176         
26177         Roo.each(b, function(tag) {
26178             if (w.indexOf(tag) > -1) {
26179                 return;
26180             }
26181             if (this.black.indexOf(tag) > -1) {
26182                 return;
26183             }
26184             this.black.push(tag);
26185             
26186         }, this);
26187         
26188         
26189         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26190         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26191         
26192         this.cwhite = [];
26193         this.cblack = [];
26194         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26195             if (b.indexOf(tag) > -1) {
26196                 return;
26197             }
26198             this.cwhite.push(tag);
26199             
26200         }, this);
26201         
26202         Roo.each(w, function(tag) {
26203             if (b.indexOf(tag) > -1) {
26204                 return;
26205             }
26206             if (this.cwhite.indexOf(tag) > -1) {
26207                 return;
26208             }
26209             this.cwhite.push(tag);
26210             
26211         }, this);
26212         
26213         
26214         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26215             if (w.indexOf(tag) > -1) {
26216                 return;
26217             }
26218             this.cblack.push(tag);
26219             
26220         }, this);
26221         
26222         Roo.each(b, function(tag) {
26223             if (w.indexOf(tag) > -1) {
26224                 return;
26225             }
26226             if (this.cblack.indexOf(tag) > -1) {
26227                 return;
26228             }
26229             this.cblack.push(tag);
26230             
26231         }, this);
26232     },
26233     
26234     setStylesheets : function(stylesheets)
26235     {
26236         if(typeof(stylesheets) == 'string'){
26237             Roo.get(this.iframe.contentDocument.head).createChild({
26238                 tag : 'link',
26239                 rel : 'stylesheet',
26240                 type : 'text/css',
26241                 href : stylesheets
26242             });
26243             
26244             return;
26245         }
26246         var _this = this;
26247      
26248         Roo.each(stylesheets, function(s) {
26249             if(!s.length){
26250                 return;
26251             }
26252             
26253             Roo.get(_this.iframe.contentDocument.head).createChild({
26254                 tag : 'link',
26255                 rel : 'stylesheet',
26256                 type : 'text/css',
26257                 href : s
26258             });
26259         });
26260
26261         
26262     },
26263     
26264     removeStylesheets : function()
26265     {
26266         var _this = this;
26267         
26268         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26269             s.remove();
26270         });
26271     }
26272     
26273     // hide stuff that is not compatible
26274     /**
26275      * @event blur
26276      * @hide
26277      */
26278     /**
26279      * @event change
26280      * @hide
26281      */
26282     /**
26283      * @event focus
26284      * @hide
26285      */
26286     /**
26287      * @event specialkey
26288      * @hide
26289      */
26290     /**
26291      * @cfg {String} fieldClass @hide
26292      */
26293     /**
26294      * @cfg {String} focusClass @hide
26295      */
26296     /**
26297      * @cfg {String} autoCreate @hide
26298      */
26299     /**
26300      * @cfg {String} inputType @hide
26301      */
26302     /**
26303      * @cfg {String} invalidClass @hide
26304      */
26305     /**
26306      * @cfg {String} invalidText @hide
26307      */
26308     /**
26309      * @cfg {String} msgFx @hide
26310      */
26311     /**
26312      * @cfg {String} validateOnBlur @hide
26313      */
26314 });
26315
26316 Roo.HtmlEditorCore.white = [
26317         'area', 'br', 'img', 'input', 'hr', 'wbr',
26318         
26319        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26320        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26321        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26322        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26323        'table',   'ul',         'xmp', 
26324        
26325        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26326       'thead',   'tr', 
26327      
26328       'dir', 'menu', 'ol', 'ul', 'dl',
26329        
26330       'embed',  'object'
26331 ];
26332
26333
26334 Roo.HtmlEditorCore.black = [
26335     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26336         'applet', // 
26337         'base',   'basefont', 'bgsound', 'blink',  'body', 
26338         'frame',  'frameset', 'head',    'html',   'ilayer', 
26339         'iframe', 'layer',  'link',     'meta',    'object',   
26340         'script', 'style' ,'title',  'xml' // clean later..
26341 ];
26342 Roo.HtmlEditorCore.clean = [
26343     'script', 'style', 'title', 'xml'
26344 ];
26345 Roo.HtmlEditorCore.remove = [
26346     'font'
26347 ];
26348 // attributes..
26349
26350 Roo.HtmlEditorCore.ablack = [
26351     'on'
26352 ];
26353     
26354 Roo.HtmlEditorCore.aclean = [ 
26355     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26356 ];
26357
26358 // protocols..
26359 Roo.HtmlEditorCore.pwhite= [
26360         'http',  'https',  'mailto'
26361 ];
26362
26363 // white listed style attributes.
26364 Roo.HtmlEditorCore.cwhite= [
26365       //  'text-align', /// default is to allow most things..
26366       
26367          
26368 //        'font-size'//??
26369 ];
26370
26371 // black listed style attributes.
26372 Roo.HtmlEditorCore.cblack= [
26373       //  'font-size' -- this can be set by the project 
26374 ];
26375
26376
26377 Roo.HtmlEditorCore.swapCodes   =[ 
26378     [    8211, "--" ], 
26379     [    8212, "--" ], 
26380     [    8216,  "'" ],  
26381     [    8217, "'" ],  
26382     [    8220, '"' ],  
26383     [    8221, '"' ],  
26384     [    8226, "*" ],  
26385     [    8230, "..." ]
26386 ]; 
26387
26388     //<script type="text/javascript">
26389
26390 /*
26391  * Ext JS Library 1.1.1
26392  * Copyright(c) 2006-2007, Ext JS, LLC.
26393  * Licence LGPL
26394  * 
26395  */
26396  
26397  
26398 Roo.form.HtmlEditor = function(config){
26399     
26400     
26401     
26402     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26403     
26404     if (!this.toolbars) {
26405         this.toolbars = [];
26406     }
26407     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26408     
26409     
26410 };
26411
26412 /**
26413  * @class Roo.form.HtmlEditor
26414  * @extends Roo.form.Field
26415  * Provides a lightweight HTML Editor component.
26416  *
26417  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26418  * 
26419  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26420  * supported by this editor.</b><br/><br/>
26421  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26422  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26423  */
26424 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26425     /**
26426      * @cfg {Boolean} clearUp
26427      */
26428     clearUp : true,
26429       /**
26430      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26431      */
26432     toolbars : false,
26433    
26434      /**
26435      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26436      *                        Roo.resizable.
26437      */
26438     resizable : false,
26439      /**
26440      * @cfg {Number} height (in pixels)
26441      */   
26442     height: 300,
26443    /**
26444      * @cfg {Number} width (in pixels)
26445      */   
26446     width: 500,
26447     
26448     /**
26449      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26450      * 
26451      */
26452     stylesheets: false,
26453     
26454     
26455      /**
26456      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26457      * 
26458      */
26459     cblack: false,
26460     /**
26461      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26462      * 
26463      */
26464     cwhite: false,
26465     
26466      /**
26467      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26468      * 
26469      */
26470     black: false,
26471     /**
26472      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26473      * 
26474      */
26475     white: false,
26476     
26477     // id of frame..
26478     frameId: false,
26479     
26480     // private properties
26481     validationEvent : false,
26482     deferHeight: true,
26483     initialized : false,
26484     activated : false,
26485     
26486     onFocus : Roo.emptyFn,
26487     iframePad:3,
26488     hideMode:'offsets',
26489     
26490     actionMode : 'container', // defaults to hiding it...
26491     
26492     defaultAutoCreate : { // modified by initCompnoent..
26493         tag: "textarea",
26494         style:"width:500px;height:300px;",
26495         autocomplete: "new-password"
26496     },
26497
26498     // private
26499     initComponent : function(){
26500         this.addEvents({
26501             /**
26502              * @event initialize
26503              * Fires when the editor is fully initialized (including the iframe)
26504              * @param {HtmlEditor} this
26505              */
26506             initialize: true,
26507             /**
26508              * @event activate
26509              * Fires when the editor is first receives the focus. Any insertion must wait
26510              * until after this event.
26511              * @param {HtmlEditor} this
26512              */
26513             activate: true,
26514              /**
26515              * @event beforesync
26516              * Fires before the textarea is updated with content from the editor iframe. Return false
26517              * to cancel the sync.
26518              * @param {HtmlEditor} this
26519              * @param {String} html
26520              */
26521             beforesync: true,
26522              /**
26523              * @event beforepush
26524              * Fires before the iframe editor is updated with content from the textarea. Return false
26525              * to cancel the push.
26526              * @param {HtmlEditor} this
26527              * @param {String} html
26528              */
26529             beforepush: true,
26530              /**
26531              * @event sync
26532              * Fires when the textarea is updated with content from the editor iframe.
26533              * @param {HtmlEditor} this
26534              * @param {String} html
26535              */
26536             sync: true,
26537              /**
26538              * @event push
26539              * Fires when the iframe editor is updated with content from the textarea.
26540              * @param {HtmlEditor} this
26541              * @param {String} html
26542              */
26543             push: true,
26544              /**
26545              * @event editmodechange
26546              * Fires when the editor switches edit modes
26547              * @param {HtmlEditor} this
26548              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26549              */
26550             editmodechange: true,
26551             /**
26552              * @event editorevent
26553              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26554              * @param {HtmlEditor} this
26555              */
26556             editorevent: true,
26557             /**
26558              * @event firstfocus
26559              * Fires when on first focus - needed by toolbars..
26560              * @param {HtmlEditor} this
26561              */
26562             firstfocus: true,
26563             /**
26564              * @event autosave
26565              * Auto save the htmlEditor value as a file into Events
26566              * @param {HtmlEditor} this
26567              */
26568             autosave: true,
26569             /**
26570              * @event savedpreview
26571              * preview the saved version of htmlEditor
26572              * @param {HtmlEditor} this
26573              */
26574             savedpreview: true,
26575             
26576             /**
26577             * @event stylesheetsclick
26578             * Fires when press the Sytlesheets button
26579             * @param {Roo.HtmlEditorCore} this
26580             */
26581             stylesheetsclick: true
26582         });
26583         this.defaultAutoCreate =  {
26584             tag: "textarea",
26585             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26586             autocomplete: "new-password"
26587         };
26588     },
26589
26590     /**
26591      * Protected method that will not generally be called directly. It
26592      * is called when the editor creates its toolbar. Override this method if you need to
26593      * add custom toolbar buttons.
26594      * @param {HtmlEditor} editor
26595      */
26596     createToolbar : function(editor){
26597         Roo.log("create toolbars");
26598         if (!editor.toolbars || !editor.toolbars.length) {
26599             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26600         }
26601         
26602         for (var i =0 ; i < editor.toolbars.length;i++) {
26603             editor.toolbars[i] = Roo.factory(
26604                     typeof(editor.toolbars[i]) == 'string' ?
26605                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26606                 Roo.form.HtmlEditor);
26607             editor.toolbars[i].init(editor);
26608         }
26609          
26610         
26611     },
26612
26613      
26614     // private
26615     onRender : function(ct, position)
26616     {
26617         var _t = this;
26618         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26619         
26620         this.wrap = this.el.wrap({
26621             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26622         });
26623         
26624         this.editorcore.onRender(ct, position);
26625          
26626         if (this.resizable) {
26627             this.resizeEl = new Roo.Resizable(this.wrap, {
26628                 pinned : true,
26629                 wrap: true,
26630                 dynamic : true,
26631                 minHeight : this.height,
26632                 height: this.height,
26633                 handles : this.resizable,
26634                 width: this.width,
26635                 listeners : {
26636                     resize : function(r, w, h) {
26637                         _t.onResize(w,h); // -something
26638                     }
26639                 }
26640             });
26641             
26642         }
26643         this.createToolbar(this);
26644        
26645         
26646         if(!this.width){
26647             this.setSize(this.wrap.getSize());
26648         }
26649         if (this.resizeEl) {
26650             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26651             // should trigger onReize..
26652         }
26653         
26654         this.keyNav = new Roo.KeyNav(this.el, {
26655             
26656             "tab" : function(e){
26657                 e.preventDefault();
26658                 
26659                 var value = this.getValue();
26660                 
26661                 var start = this.el.dom.selectionStart;
26662                 var end = this.el.dom.selectionEnd;
26663                 
26664                 if(!e.shiftKey){
26665                     
26666                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26667                     this.el.dom.setSelectionRange(end + 1, end + 1);
26668                     return;
26669                 }
26670                 
26671                 var f = value.substring(0, start).split("\t");
26672                 
26673                 if(f.pop().length != 0){
26674                     return;
26675                 }
26676                 
26677                 this.setValue(f.join("\t") + value.substring(end));
26678                 this.el.dom.setSelectionRange(start - 1, start - 1);
26679                 
26680             },
26681             
26682             "home" : function(e){
26683                 e.preventDefault();
26684                 
26685                 var curr = this.el.dom.selectionStart;
26686                 var lines = this.getValue().split("\n");
26687                 
26688                 if(!lines.length){
26689                     return;
26690                 }
26691                 
26692                 if(e.ctrlKey){
26693                     this.el.dom.setSelectionRange(0, 0);
26694                     return;
26695                 }
26696                 
26697                 var pos = 0;
26698                 
26699                 for (var i = 0; i < lines.length;i++) {
26700                     pos += lines[i].length;
26701                     
26702                     if(i != 0){
26703                         pos += 1;
26704                     }
26705                     
26706                     if(pos < curr){
26707                         continue;
26708                     }
26709                     
26710                     pos -= lines[i].length;
26711                     
26712                     break;
26713                 }
26714                 
26715                 if(!e.shiftKey){
26716                     this.el.dom.setSelectionRange(pos, pos);
26717                     return;
26718                 }
26719                 
26720                 this.el.dom.selectionStart = pos;
26721                 this.el.dom.selectionEnd = curr;
26722             },
26723             
26724             "end" : function(e){
26725                 e.preventDefault();
26726                 
26727                 var curr = this.el.dom.selectionStart;
26728                 var lines = this.getValue().split("\n");
26729                 
26730                 if(!lines.length){
26731                     return;
26732                 }
26733                 
26734                 if(e.ctrlKey){
26735                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26736                     return;
26737                 }
26738                 
26739                 var pos = 0;
26740                 
26741                 for (var i = 0; i < lines.length;i++) {
26742                     
26743                     pos += lines[i].length;
26744                     
26745                     if(i != 0){
26746                         pos += 1;
26747                     }
26748                     
26749                     if(pos < curr){
26750                         continue;
26751                     }
26752                     
26753                     break;
26754                 }
26755                 
26756                 if(!e.shiftKey){
26757                     this.el.dom.setSelectionRange(pos, pos);
26758                     return;
26759                 }
26760                 
26761                 this.el.dom.selectionStart = curr;
26762                 this.el.dom.selectionEnd = pos;
26763             },
26764
26765             scope : this,
26766
26767             doRelay : function(foo, bar, hname){
26768                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26769             },
26770
26771             forceKeyDown: true
26772         });
26773         
26774 //        if(this.autosave && this.w){
26775 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26776 //        }
26777     },
26778
26779     // private
26780     onResize : function(w, h)
26781     {
26782         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26783         var ew = false;
26784         var eh = false;
26785         
26786         if(this.el ){
26787             if(typeof w == 'number'){
26788                 var aw = w - this.wrap.getFrameWidth('lr');
26789                 this.el.setWidth(this.adjustWidth('textarea', aw));
26790                 ew = aw;
26791             }
26792             if(typeof h == 'number'){
26793                 var tbh = 0;
26794                 for (var i =0; i < this.toolbars.length;i++) {
26795                     // fixme - ask toolbars for heights?
26796                     tbh += this.toolbars[i].tb.el.getHeight();
26797                     if (this.toolbars[i].footer) {
26798                         tbh += this.toolbars[i].footer.el.getHeight();
26799                     }
26800                 }
26801                 
26802                 
26803                 
26804                 
26805                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26806                 ah -= 5; // knock a few pixes off for look..
26807 //                Roo.log(ah);
26808                 this.el.setHeight(this.adjustWidth('textarea', ah));
26809                 var eh = ah;
26810             }
26811         }
26812         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26813         this.editorcore.onResize(ew,eh);
26814         
26815     },
26816
26817     /**
26818      * Toggles the editor between standard and source edit mode.
26819      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26820      */
26821     toggleSourceEdit : function(sourceEditMode)
26822     {
26823         this.editorcore.toggleSourceEdit(sourceEditMode);
26824         
26825         if(this.editorcore.sourceEditMode){
26826             Roo.log('editor - showing textarea');
26827             
26828 //            Roo.log('in');
26829 //            Roo.log(this.syncValue());
26830             this.editorcore.syncValue();
26831             this.el.removeClass('x-hidden');
26832             this.el.dom.removeAttribute('tabIndex');
26833             this.el.focus();
26834             
26835             for (var i = 0; i < this.toolbars.length; i++) {
26836                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26837                     this.toolbars[i].tb.hide();
26838                     this.toolbars[i].footer.hide();
26839                 }
26840             }
26841             
26842         }else{
26843             Roo.log('editor - hiding textarea');
26844 //            Roo.log('out')
26845 //            Roo.log(this.pushValue()); 
26846             this.editorcore.pushValue();
26847             
26848             this.el.addClass('x-hidden');
26849             this.el.dom.setAttribute('tabIndex', -1);
26850             
26851             for (var i = 0; i < this.toolbars.length; i++) {
26852                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26853                     this.toolbars[i].tb.show();
26854                     this.toolbars[i].footer.show();
26855                 }
26856             }
26857             
26858             //this.deferFocus();
26859         }
26860         
26861         this.setSize(this.wrap.getSize());
26862         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26863         
26864         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26865     },
26866  
26867     // private (for BoxComponent)
26868     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26869
26870     // private (for BoxComponent)
26871     getResizeEl : function(){
26872         return this.wrap;
26873     },
26874
26875     // private (for BoxComponent)
26876     getPositionEl : function(){
26877         return this.wrap;
26878     },
26879
26880     // private
26881     initEvents : function(){
26882         this.originalValue = this.getValue();
26883     },
26884
26885     /**
26886      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26887      * @method
26888      */
26889     markInvalid : Roo.emptyFn,
26890     /**
26891      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26892      * @method
26893      */
26894     clearInvalid : Roo.emptyFn,
26895
26896     setValue : function(v){
26897         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26898         this.editorcore.pushValue();
26899     },
26900
26901      
26902     // private
26903     deferFocus : function(){
26904         this.focus.defer(10, this);
26905     },
26906
26907     // doc'ed in Field
26908     focus : function(){
26909         this.editorcore.focus();
26910         
26911     },
26912       
26913
26914     // private
26915     onDestroy : function(){
26916         
26917         
26918         
26919         if(this.rendered){
26920             
26921             for (var i =0; i < this.toolbars.length;i++) {
26922                 // fixme - ask toolbars for heights?
26923                 this.toolbars[i].onDestroy();
26924             }
26925             
26926             this.wrap.dom.innerHTML = '';
26927             this.wrap.remove();
26928         }
26929     },
26930
26931     // private
26932     onFirstFocus : function(){
26933         //Roo.log("onFirstFocus");
26934         this.editorcore.onFirstFocus();
26935          for (var i =0; i < this.toolbars.length;i++) {
26936             this.toolbars[i].onFirstFocus();
26937         }
26938         
26939     },
26940     
26941     // private
26942     syncValue : function()
26943     {
26944         this.editorcore.syncValue();
26945     },
26946     
26947     pushValue : function()
26948     {
26949         this.editorcore.pushValue();
26950     },
26951     
26952     setStylesheets : function(stylesheets)
26953     {
26954         this.editorcore.setStylesheets(stylesheets);
26955     },
26956     
26957     removeStylesheets : function()
26958     {
26959         this.editorcore.removeStylesheets();
26960     }
26961      
26962     
26963     // hide stuff that is not compatible
26964     /**
26965      * @event blur
26966      * @hide
26967      */
26968     /**
26969      * @event change
26970      * @hide
26971      */
26972     /**
26973      * @event focus
26974      * @hide
26975      */
26976     /**
26977      * @event specialkey
26978      * @hide
26979      */
26980     /**
26981      * @cfg {String} fieldClass @hide
26982      */
26983     /**
26984      * @cfg {String} focusClass @hide
26985      */
26986     /**
26987      * @cfg {String} autoCreate @hide
26988      */
26989     /**
26990      * @cfg {String} inputType @hide
26991      */
26992     /**
26993      * @cfg {String} invalidClass @hide
26994      */
26995     /**
26996      * @cfg {String} invalidText @hide
26997      */
26998     /**
26999      * @cfg {String} msgFx @hide
27000      */
27001     /**
27002      * @cfg {String} validateOnBlur @hide
27003      */
27004 });
27005  
27006     // <script type="text/javascript">
27007 /*
27008  * Based on
27009  * Ext JS Library 1.1.1
27010  * Copyright(c) 2006-2007, Ext JS, LLC.
27011  *  
27012  
27013  */
27014
27015 /**
27016  * @class Roo.form.HtmlEditorToolbar1
27017  * Basic Toolbar
27018  * 
27019  * Usage:
27020  *
27021  new Roo.form.HtmlEditor({
27022     ....
27023     toolbars : [
27024         new Roo.form.HtmlEditorToolbar1({
27025             disable : { fonts: 1 , format: 1, ..., ... , ...],
27026             btns : [ .... ]
27027         })
27028     }
27029      
27030  * 
27031  * @cfg {Object} disable List of elements to disable..
27032  * @cfg {Array} btns List of additional buttons.
27033  * 
27034  * 
27035  * NEEDS Extra CSS? 
27036  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27037  */
27038  
27039 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27040 {
27041     
27042     Roo.apply(this, config);
27043     
27044     // default disabled, based on 'good practice'..
27045     this.disable = this.disable || {};
27046     Roo.applyIf(this.disable, {
27047         fontSize : true,
27048         colors : true,
27049         specialElements : true
27050     });
27051     
27052     
27053     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27054     // dont call parent... till later.
27055 }
27056
27057 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
27058     
27059     tb: false,
27060     
27061     rendered: false,
27062     
27063     editor : false,
27064     editorcore : false,
27065     /**
27066      * @cfg {Object} disable  List of toolbar elements to disable
27067          
27068      */
27069     disable : false,
27070     
27071     
27072      /**
27073      * @cfg {String} createLinkText The default text for the create link prompt
27074      */
27075     createLinkText : 'Please enter the URL for the link:',
27076     /**
27077      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27078      */
27079     defaultLinkValue : 'http:/'+'/',
27080    
27081     
27082       /**
27083      * @cfg {Array} fontFamilies An array of available font families
27084      */
27085     fontFamilies : [
27086         'Arial',
27087         'Courier New',
27088         'Tahoma',
27089         'Times New Roman',
27090         'Verdana'
27091     ],
27092     
27093     specialChars : [
27094            "&#169;",
27095           "&#174;",     
27096           "&#8482;",    
27097           "&#163;" ,    
27098          // "&#8212;",    
27099           "&#8230;",    
27100           "&#247;" ,    
27101         //  "&#225;" ,     ?? a acute?
27102            "&#8364;"    , //Euro
27103        //   "&#8220;"    ,
27104         //  "&#8221;"    ,
27105         //  "&#8226;"    ,
27106           "&#176;"  //   , // degrees
27107
27108          // "&#233;"     , // e ecute
27109          // "&#250;"     , // u ecute?
27110     ],
27111     
27112     specialElements : [
27113         {
27114             text: "Insert Table",
27115             xtype: 'MenuItem',
27116             xns : Roo.Menu,
27117             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27118                 
27119         },
27120         {    
27121             text: "Insert Image",
27122             xtype: 'MenuItem',
27123             xns : Roo.Menu,
27124             ihtml : '<img src="about:blank"/>'
27125             
27126         }
27127         
27128          
27129     ],
27130     
27131     
27132     inputElements : [ 
27133             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27134             "input:submit", "input:button", "select", "textarea", "label" ],
27135     formats : [
27136         ["p"] ,  
27137         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27138         ["pre"],[ "code"], 
27139         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27140         ['div'],['span']
27141     ],
27142     
27143     cleanStyles : [
27144         "font-size"
27145     ],
27146      /**
27147      * @cfg {String} defaultFont default font to use.
27148      */
27149     defaultFont: 'tahoma',
27150    
27151     fontSelect : false,
27152     
27153     
27154     formatCombo : false,
27155     
27156     init : function(editor)
27157     {
27158         this.editor = editor;
27159         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27160         var editorcore = this.editorcore;
27161         
27162         var _t = this;
27163         
27164         var fid = editorcore.frameId;
27165         var etb = this;
27166         function btn(id, toggle, handler){
27167             var xid = fid + '-'+ id ;
27168             return {
27169                 id : xid,
27170                 cmd : id,
27171                 cls : 'x-btn-icon x-edit-'+id,
27172                 enableToggle:toggle !== false,
27173                 scope: _t, // was editor...
27174                 handler:handler||_t.relayBtnCmd,
27175                 clickEvent:'mousedown',
27176                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27177                 tabIndex:-1
27178             };
27179         }
27180         
27181         
27182         
27183         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27184         this.tb = tb;
27185          // stop form submits
27186         tb.el.on('click', function(e){
27187             e.preventDefault(); // what does this do?
27188         });
27189
27190         if(!this.disable.font) { // && !Roo.isSafari){
27191             /* why no safari for fonts 
27192             editor.fontSelect = tb.el.createChild({
27193                 tag:'select',
27194                 tabIndex: -1,
27195                 cls:'x-font-select',
27196                 html: this.createFontOptions()
27197             });
27198             
27199             editor.fontSelect.on('change', function(){
27200                 var font = editor.fontSelect.dom.value;
27201                 editor.relayCmd('fontname', font);
27202                 editor.deferFocus();
27203             }, editor);
27204             
27205             tb.add(
27206                 editor.fontSelect.dom,
27207                 '-'
27208             );
27209             */
27210             
27211         };
27212         if(!this.disable.formats){
27213             this.formatCombo = new Roo.form.ComboBox({
27214                 store: new Roo.data.SimpleStore({
27215                     id : 'tag',
27216                     fields: ['tag'],
27217                     data : this.formats // from states.js
27218                 }),
27219                 blockFocus : true,
27220                 name : '',
27221                 //autoCreate : {tag: "div",  size: "20"},
27222                 displayField:'tag',
27223                 typeAhead: false,
27224                 mode: 'local',
27225                 editable : false,
27226                 triggerAction: 'all',
27227                 emptyText:'Add tag',
27228                 selectOnFocus:true,
27229                 width:135,
27230                 listeners : {
27231                     'select': function(c, r, i) {
27232                         editorcore.insertTag(r.get('tag'));
27233                         editor.focus();
27234                     }
27235                 }
27236
27237             });
27238             tb.addField(this.formatCombo);
27239             
27240         }
27241         
27242         if(!this.disable.format){
27243             tb.add(
27244                 btn('bold'),
27245                 btn('italic'),
27246                 btn('underline'),
27247                 btn('strikethrough')
27248             );
27249         };
27250         if(!this.disable.fontSize){
27251             tb.add(
27252                 '-',
27253                 
27254                 
27255                 btn('increasefontsize', false, editorcore.adjustFont),
27256                 btn('decreasefontsize', false, editorcore.adjustFont)
27257             );
27258         };
27259         
27260         
27261         if(!this.disable.colors){
27262             tb.add(
27263                 '-', {
27264                     id:editorcore.frameId +'-forecolor',
27265                     cls:'x-btn-icon x-edit-forecolor',
27266                     clickEvent:'mousedown',
27267                     tooltip: this.buttonTips['forecolor'] || undefined,
27268                     tabIndex:-1,
27269                     menu : new Roo.menu.ColorMenu({
27270                         allowReselect: true,
27271                         focus: Roo.emptyFn,
27272                         value:'000000',
27273                         plain:true,
27274                         selectHandler: function(cp, color){
27275                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27276                             editor.deferFocus();
27277                         },
27278                         scope: editorcore,
27279                         clickEvent:'mousedown'
27280                     })
27281                 }, {
27282                     id:editorcore.frameId +'backcolor',
27283                     cls:'x-btn-icon x-edit-backcolor',
27284                     clickEvent:'mousedown',
27285                     tooltip: this.buttonTips['backcolor'] || undefined,
27286                     tabIndex:-1,
27287                     menu : new Roo.menu.ColorMenu({
27288                         focus: Roo.emptyFn,
27289                         value:'FFFFFF',
27290                         plain:true,
27291                         allowReselect: true,
27292                         selectHandler: function(cp, color){
27293                             if(Roo.isGecko){
27294                                 editorcore.execCmd('useCSS', false);
27295                                 editorcore.execCmd('hilitecolor', color);
27296                                 editorcore.execCmd('useCSS', true);
27297                                 editor.deferFocus();
27298                             }else{
27299                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27300                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27301                                 editor.deferFocus();
27302                             }
27303                         },
27304                         scope:editorcore,
27305                         clickEvent:'mousedown'
27306                     })
27307                 }
27308             );
27309         };
27310         // now add all the items...
27311         
27312
27313         if(!this.disable.alignments){
27314             tb.add(
27315                 '-',
27316                 btn('justifyleft'),
27317                 btn('justifycenter'),
27318                 btn('justifyright')
27319             );
27320         };
27321
27322         //if(!Roo.isSafari){
27323             if(!this.disable.links){
27324                 tb.add(
27325                     '-',
27326                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27327                 );
27328             };
27329
27330             if(!this.disable.lists){
27331                 tb.add(
27332                     '-',
27333                     btn('insertorderedlist'),
27334                     btn('insertunorderedlist')
27335                 );
27336             }
27337             if(!this.disable.sourceEdit){
27338                 tb.add(
27339                     '-',
27340                     btn('sourceedit', true, function(btn){
27341                         this.toggleSourceEdit(btn.pressed);
27342                     })
27343                 );
27344             }
27345         //}
27346         
27347         var smenu = { };
27348         // special menu.. - needs to be tidied up..
27349         if (!this.disable.special) {
27350             smenu = {
27351                 text: "&#169;",
27352                 cls: 'x-edit-none',
27353                 
27354                 menu : {
27355                     items : []
27356                 }
27357             };
27358             for (var i =0; i < this.specialChars.length; i++) {
27359                 smenu.menu.items.push({
27360                     
27361                     html: this.specialChars[i],
27362                     handler: function(a,b) {
27363                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27364                         //editor.insertAtCursor(a.html);
27365                         
27366                     },
27367                     tabIndex:-1
27368                 });
27369             }
27370             
27371             
27372             tb.add(smenu);
27373             
27374             
27375         }
27376         
27377         var cmenu = { };
27378         if (!this.disable.cleanStyles) {
27379             cmenu = {
27380                 cls: 'x-btn-icon x-btn-clear',
27381                 
27382                 menu : {
27383                     items : []
27384                 }
27385             };
27386             for (var i =0; i < this.cleanStyles.length; i++) {
27387                 cmenu.menu.items.push({
27388                     actiontype : this.cleanStyles[i],
27389                     html: 'Remove ' + this.cleanStyles[i],
27390                     handler: function(a,b) {
27391 //                        Roo.log(a);
27392 //                        Roo.log(b);
27393                         var c = Roo.get(editorcore.doc.body);
27394                         c.select('[style]').each(function(s) {
27395                             s.dom.style.removeProperty(a.actiontype);
27396                         });
27397                         editorcore.syncValue();
27398                     },
27399                     tabIndex:-1
27400                 });
27401             }
27402              cmenu.menu.items.push({
27403                 actiontype : 'tablewidths',
27404                 html: 'Remove Table Widths',
27405                 handler: function(a,b) {
27406                     editorcore.cleanTableWidths();
27407                     editorcore.syncValue();
27408                 },
27409                 tabIndex:-1
27410             });
27411             cmenu.menu.items.push({
27412                 actiontype : 'word',
27413                 html: 'Remove MS Word Formating',
27414                 handler: function(a,b) {
27415                     editorcore.cleanWord();
27416                     editorcore.syncValue();
27417                 },
27418                 tabIndex:-1
27419             });
27420             
27421             cmenu.menu.items.push({
27422                 actiontype : 'all',
27423                 html: 'Remove All Styles',
27424                 handler: function(a,b) {
27425                     
27426                     var c = Roo.get(editorcore.doc.body);
27427                     c.select('[style]').each(function(s) {
27428                         s.dom.removeAttribute('style');
27429                     });
27430                     editorcore.syncValue();
27431                 },
27432                 tabIndex:-1
27433             });
27434             
27435             cmenu.menu.items.push({
27436                 actiontype : 'all',
27437                 html: 'Remove All CSS Classes',
27438                 handler: function(a,b) {
27439                     
27440                     var c = Roo.get(editorcore.doc.body);
27441                     c.select('[class]').each(function(s) {
27442                         s.dom.className = '';
27443                     });
27444                     editorcore.syncValue();
27445                 },
27446                 tabIndex:-1
27447             });
27448             
27449              cmenu.menu.items.push({
27450                 actiontype : 'tidy',
27451                 html: 'Tidy HTML Source',
27452                 handler: function(a,b) {
27453                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27454                     editorcore.syncValue();
27455                 },
27456                 tabIndex:-1
27457             });
27458             
27459             
27460             tb.add(cmenu);
27461         }
27462          
27463         if (!this.disable.specialElements) {
27464             var semenu = {
27465                 text: "Other;",
27466                 cls: 'x-edit-none',
27467                 menu : {
27468                     items : []
27469                 }
27470             };
27471             for (var i =0; i < this.specialElements.length; i++) {
27472                 semenu.menu.items.push(
27473                     Roo.apply({ 
27474                         handler: function(a,b) {
27475                             editor.insertAtCursor(this.ihtml);
27476                         }
27477                     }, this.specialElements[i])
27478                 );
27479                     
27480             }
27481             
27482             tb.add(semenu);
27483             
27484             
27485         }
27486          
27487         
27488         if (this.btns) {
27489             for(var i =0; i< this.btns.length;i++) {
27490                 var b = Roo.factory(this.btns[i],Roo.form);
27491                 b.cls =  'x-edit-none';
27492                 
27493                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27494                     b.cls += ' x-init-enable';
27495                 }
27496                 
27497                 b.scope = editorcore;
27498                 tb.add(b);
27499             }
27500         
27501         }
27502         
27503         
27504         
27505         // disable everything...
27506         
27507         this.tb.items.each(function(item){
27508             
27509            if(
27510                 item.id != editorcore.frameId+ '-sourceedit' && 
27511                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27512             ){
27513                 
27514                 item.disable();
27515             }
27516         });
27517         this.rendered = true;
27518         
27519         // the all the btns;
27520         editor.on('editorevent', this.updateToolbar, this);
27521         // other toolbars need to implement this..
27522         //editor.on('editmodechange', this.updateToolbar, this);
27523     },
27524     
27525     
27526     relayBtnCmd : function(btn) {
27527         this.editorcore.relayCmd(btn.cmd);
27528     },
27529     // private used internally
27530     createLink : function(){
27531         Roo.log("create link?");
27532         var url = prompt(this.createLinkText, this.defaultLinkValue);
27533         if(url && url != 'http:/'+'/'){
27534             this.editorcore.relayCmd('createlink', url);
27535         }
27536     },
27537
27538     
27539     /**
27540      * Protected method that will not generally be called directly. It triggers
27541      * a toolbar update by reading the markup state of the current selection in the editor.
27542      */
27543     updateToolbar: function(){
27544
27545         if(!this.editorcore.activated){
27546             this.editor.onFirstFocus();
27547             return;
27548         }
27549
27550         var btns = this.tb.items.map, 
27551             doc = this.editorcore.doc,
27552             frameId = this.editorcore.frameId;
27553
27554         if(!this.disable.font && !Roo.isSafari){
27555             /*
27556             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27557             if(name != this.fontSelect.dom.value){
27558                 this.fontSelect.dom.value = name;
27559             }
27560             */
27561         }
27562         if(!this.disable.format){
27563             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27564             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27565             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27566             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27567         }
27568         if(!this.disable.alignments){
27569             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27570             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27571             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27572         }
27573         if(!Roo.isSafari && !this.disable.lists){
27574             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27575             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27576         }
27577         
27578         var ans = this.editorcore.getAllAncestors();
27579         if (this.formatCombo) {
27580             
27581             
27582             var store = this.formatCombo.store;
27583             this.formatCombo.setValue("");
27584             for (var i =0; i < ans.length;i++) {
27585                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27586                     // select it..
27587                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27588                     break;
27589                 }
27590             }
27591         }
27592         
27593         
27594         
27595         // hides menus... - so this cant be on a menu...
27596         Roo.menu.MenuMgr.hideAll();
27597
27598         //this.editorsyncValue();
27599     },
27600    
27601     
27602     createFontOptions : function(){
27603         var buf = [], fs = this.fontFamilies, ff, lc;
27604         
27605         
27606         
27607         for(var i = 0, len = fs.length; i< len; i++){
27608             ff = fs[i];
27609             lc = ff.toLowerCase();
27610             buf.push(
27611                 '<option value="',lc,'" style="font-family:',ff,';"',
27612                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27613                     ff,
27614                 '</option>'
27615             );
27616         }
27617         return buf.join('');
27618     },
27619     
27620     toggleSourceEdit : function(sourceEditMode){
27621         
27622         Roo.log("toolbar toogle");
27623         if(sourceEditMode === undefined){
27624             sourceEditMode = !this.sourceEditMode;
27625         }
27626         this.sourceEditMode = sourceEditMode === true;
27627         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27628         // just toggle the button?
27629         if(btn.pressed !== this.sourceEditMode){
27630             btn.toggle(this.sourceEditMode);
27631             return;
27632         }
27633         
27634         if(sourceEditMode){
27635             Roo.log("disabling buttons");
27636             this.tb.items.each(function(item){
27637                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27638                     item.disable();
27639                 }
27640             });
27641           
27642         }else{
27643             Roo.log("enabling buttons");
27644             if(this.editorcore.initialized){
27645                 this.tb.items.each(function(item){
27646                     item.enable();
27647                 });
27648             }
27649             
27650         }
27651         Roo.log("calling toggole on editor");
27652         // tell the editor that it's been pressed..
27653         this.editor.toggleSourceEdit(sourceEditMode);
27654        
27655     },
27656      /**
27657      * Object collection of toolbar tooltips for the buttons in the editor. The key
27658      * is the command id associated with that button and the value is a valid QuickTips object.
27659      * For example:
27660 <pre><code>
27661 {
27662     bold : {
27663         title: 'Bold (Ctrl+B)',
27664         text: 'Make the selected text bold.',
27665         cls: 'x-html-editor-tip'
27666     },
27667     italic : {
27668         title: 'Italic (Ctrl+I)',
27669         text: 'Make the selected text italic.',
27670         cls: 'x-html-editor-tip'
27671     },
27672     ...
27673 </code></pre>
27674     * @type Object
27675      */
27676     buttonTips : {
27677         bold : {
27678             title: 'Bold (Ctrl+B)',
27679             text: 'Make the selected text bold.',
27680             cls: 'x-html-editor-tip'
27681         },
27682         italic : {
27683             title: 'Italic (Ctrl+I)',
27684             text: 'Make the selected text italic.',
27685             cls: 'x-html-editor-tip'
27686         },
27687         underline : {
27688             title: 'Underline (Ctrl+U)',
27689             text: 'Underline the selected text.',
27690             cls: 'x-html-editor-tip'
27691         },
27692         strikethrough : {
27693             title: 'Strikethrough',
27694             text: 'Strikethrough the selected text.',
27695             cls: 'x-html-editor-tip'
27696         },
27697         increasefontsize : {
27698             title: 'Grow Text',
27699             text: 'Increase the font size.',
27700             cls: 'x-html-editor-tip'
27701         },
27702         decreasefontsize : {
27703             title: 'Shrink Text',
27704             text: 'Decrease the font size.',
27705             cls: 'x-html-editor-tip'
27706         },
27707         backcolor : {
27708             title: 'Text Highlight Color',
27709             text: 'Change the background color of the selected text.',
27710             cls: 'x-html-editor-tip'
27711         },
27712         forecolor : {
27713             title: 'Font Color',
27714             text: 'Change the color of the selected text.',
27715             cls: 'x-html-editor-tip'
27716         },
27717         justifyleft : {
27718             title: 'Align Text Left',
27719             text: 'Align text to the left.',
27720             cls: 'x-html-editor-tip'
27721         },
27722         justifycenter : {
27723             title: 'Center Text',
27724             text: 'Center text in the editor.',
27725             cls: 'x-html-editor-tip'
27726         },
27727         justifyright : {
27728             title: 'Align Text Right',
27729             text: 'Align text to the right.',
27730             cls: 'x-html-editor-tip'
27731         },
27732         insertunorderedlist : {
27733             title: 'Bullet List',
27734             text: 'Start a bulleted list.',
27735             cls: 'x-html-editor-tip'
27736         },
27737         insertorderedlist : {
27738             title: 'Numbered List',
27739             text: 'Start a numbered list.',
27740             cls: 'x-html-editor-tip'
27741         },
27742         createlink : {
27743             title: 'Hyperlink',
27744             text: 'Make the selected text a hyperlink.',
27745             cls: 'x-html-editor-tip'
27746         },
27747         sourceedit : {
27748             title: 'Source Edit',
27749             text: 'Switch to source editing mode.',
27750             cls: 'x-html-editor-tip'
27751         }
27752     },
27753     // private
27754     onDestroy : function(){
27755         if(this.rendered){
27756             
27757             this.tb.items.each(function(item){
27758                 if(item.menu){
27759                     item.menu.removeAll();
27760                     if(item.menu.el){
27761                         item.menu.el.destroy();
27762                     }
27763                 }
27764                 item.destroy();
27765             });
27766              
27767         }
27768     },
27769     onFirstFocus: function() {
27770         this.tb.items.each(function(item){
27771            item.enable();
27772         });
27773     }
27774 });
27775
27776
27777
27778
27779 // <script type="text/javascript">
27780 /*
27781  * Based on
27782  * Ext JS Library 1.1.1
27783  * Copyright(c) 2006-2007, Ext JS, LLC.
27784  *  
27785  
27786  */
27787
27788  
27789 /**
27790  * @class Roo.form.HtmlEditor.ToolbarContext
27791  * Context Toolbar
27792  * 
27793  * Usage:
27794  *
27795  new Roo.form.HtmlEditor({
27796     ....
27797     toolbars : [
27798         { xtype: 'ToolbarStandard', styles : {} }
27799         { xtype: 'ToolbarContext', disable : {} }
27800     ]
27801 })
27802
27803      
27804  * 
27805  * @config : {Object} disable List of elements to disable.. (not done yet.)
27806  * @config : {Object} styles  Map of styles available.
27807  * 
27808  */
27809
27810 Roo.form.HtmlEditor.ToolbarContext = function(config)
27811 {
27812     
27813     Roo.apply(this, config);
27814     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27815     // dont call parent... till later.
27816     this.styles = this.styles || {};
27817 }
27818
27819  
27820
27821 Roo.form.HtmlEditor.ToolbarContext.types = {
27822     'IMG' : {
27823         width : {
27824             title: "Width",
27825             width: 40
27826         },
27827         height:  {
27828             title: "Height",
27829             width: 40
27830         },
27831         align: {
27832             title: "Align",
27833             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27834             width : 80
27835             
27836         },
27837         border: {
27838             title: "Border",
27839             width: 40
27840         },
27841         alt: {
27842             title: "Alt",
27843             width: 120
27844         },
27845         src : {
27846             title: "Src",
27847             width: 220
27848         }
27849         
27850     },
27851     'A' : {
27852         name : {
27853             title: "Name",
27854             width: 50
27855         },
27856         target:  {
27857             title: "Target",
27858             width: 120
27859         },
27860         href:  {
27861             title: "Href",
27862             width: 220
27863         } // border?
27864         
27865     },
27866     'TABLE' : {
27867         rows : {
27868             title: "Rows",
27869             width: 20
27870         },
27871         cols : {
27872             title: "Cols",
27873             width: 20
27874         },
27875         width : {
27876             title: "Width",
27877             width: 40
27878         },
27879         height : {
27880             title: "Height",
27881             width: 40
27882         },
27883         border : {
27884             title: "Border",
27885             width: 20
27886         }
27887     },
27888     'TD' : {
27889         width : {
27890             title: "Width",
27891             width: 40
27892         },
27893         height : {
27894             title: "Height",
27895             width: 40
27896         },   
27897         align: {
27898             title: "Align",
27899             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27900             width: 80
27901         },
27902         valign: {
27903             title: "Valign",
27904             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27905             width: 80
27906         },
27907         colspan: {
27908             title: "Colspan",
27909             width: 20
27910             
27911         },
27912          'font-family'  : {
27913             title : "Font",
27914             style : 'fontFamily',
27915             displayField: 'display',
27916             optname : 'font-family',
27917             width: 140
27918         }
27919     },
27920     'INPUT' : {
27921         name : {
27922             title: "name",
27923             width: 120
27924         },
27925         value : {
27926             title: "Value",
27927             width: 120
27928         },
27929         width : {
27930             title: "Width",
27931             width: 40
27932         }
27933     },
27934     'LABEL' : {
27935         'for' : {
27936             title: "For",
27937             width: 120
27938         }
27939     },
27940     'TEXTAREA' : {
27941           name : {
27942             title: "name",
27943             width: 120
27944         },
27945         rows : {
27946             title: "Rows",
27947             width: 20
27948         },
27949         cols : {
27950             title: "Cols",
27951             width: 20
27952         }
27953     },
27954     'SELECT' : {
27955         name : {
27956             title: "name",
27957             width: 120
27958         },
27959         selectoptions : {
27960             title: "Options",
27961             width: 200
27962         }
27963     },
27964     
27965     // should we really allow this??
27966     // should this just be 
27967     'BODY' : {
27968         title : {
27969             title: "Title",
27970             width: 200,
27971             disabled : true
27972         }
27973     },
27974     'SPAN' : {
27975         'font-family'  : {
27976             title : "Font",
27977             style : 'fontFamily',
27978             displayField: 'display',
27979             optname : 'font-family',
27980             width: 140
27981         }
27982     },
27983     'DIV' : {
27984         'font-family'  : {
27985             title : "Font",
27986             style : 'fontFamily',
27987             displayField: 'display',
27988             optname : 'font-family',
27989             width: 140
27990         }
27991     },
27992      'P' : {
27993         'font-family'  : {
27994             title : "Font",
27995             style : 'fontFamily',
27996             displayField: 'display',
27997             optname : 'font-family',
27998             width: 140
27999         }
28000     },
28001     
28002     '*' : {
28003         // empty..
28004     }
28005
28006 };
28007
28008 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28009 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28010
28011 Roo.form.HtmlEditor.ToolbarContext.options = {
28012         'font-family'  : [ 
28013                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28014                 [ 'Courier New', 'Courier New'],
28015                 [ 'Tahoma', 'Tahoma'],
28016                 [ 'Times New Roman,serif', 'Times'],
28017                 [ 'Verdana','Verdana' ]
28018         ]
28019 };
28020
28021 // fixme - these need to be configurable..
28022  
28023
28024 //Roo.form.HtmlEditor.ToolbarContext.types
28025
28026
28027 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28028     
28029     tb: false,
28030     
28031     rendered: false,
28032     
28033     editor : false,
28034     editorcore : false,
28035     /**
28036      * @cfg {Object} disable  List of toolbar elements to disable
28037          
28038      */
28039     disable : false,
28040     /**
28041      * @cfg {Object} styles List of styles 
28042      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28043      *
28044      * These must be defined in the page, so they get rendered correctly..
28045      * .headline { }
28046      * TD.underline { }
28047      * 
28048      */
28049     styles : false,
28050     
28051     options: false,
28052     
28053     toolbars : false,
28054     
28055     init : function(editor)
28056     {
28057         this.editor = editor;
28058         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28059         var editorcore = this.editorcore;
28060         
28061         var fid = editorcore.frameId;
28062         var etb = this;
28063         function btn(id, toggle, handler){
28064             var xid = fid + '-'+ id ;
28065             return {
28066                 id : xid,
28067                 cmd : id,
28068                 cls : 'x-btn-icon x-edit-'+id,
28069                 enableToggle:toggle !== false,
28070                 scope: editorcore, // was editor...
28071                 handler:handler||editorcore.relayBtnCmd,
28072                 clickEvent:'mousedown',
28073                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28074                 tabIndex:-1
28075             };
28076         }
28077         // create a new element.
28078         var wdiv = editor.wrap.createChild({
28079                 tag: 'div'
28080             }, editor.wrap.dom.firstChild.nextSibling, true);
28081         
28082         // can we do this more than once??
28083         
28084          // stop form submits
28085       
28086  
28087         // disable everything...
28088         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28089         this.toolbars = {};
28090            
28091         for (var i in  ty) {
28092           
28093             this.toolbars[i] = this.buildToolbar(ty[i],i);
28094         }
28095         this.tb = this.toolbars.BODY;
28096         this.tb.el.show();
28097         this.buildFooter();
28098         this.footer.show();
28099         editor.on('hide', function( ) { this.footer.hide() }, this);
28100         editor.on('show', function( ) { this.footer.show() }, this);
28101         
28102          
28103         this.rendered = true;
28104         
28105         // the all the btns;
28106         editor.on('editorevent', this.updateToolbar, this);
28107         // other toolbars need to implement this..
28108         //editor.on('editmodechange', this.updateToolbar, this);
28109     },
28110     
28111     
28112     
28113     /**
28114      * Protected method that will not generally be called directly. It triggers
28115      * a toolbar update by reading the markup state of the current selection in the editor.
28116      *
28117      * Note you can force an update by calling on('editorevent', scope, false)
28118      */
28119     updateToolbar: function(editor,ev,sel){
28120
28121         //Roo.log(ev);
28122         // capture mouse up - this is handy for selecting images..
28123         // perhaps should go somewhere else...
28124         if(!this.editorcore.activated){
28125              this.editor.onFirstFocus();
28126             return;
28127         }
28128         
28129         
28130         
28131         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28132         // selectNode - might want to handle IE?
28133         if (ev &&
28134             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28135             ev.target && ev.target.tagName == 'IMG') {
28136             // they have click on an image...
28137             // let's see if we can change the selection...
28138             sel = ev.target;
28139          
28140               var nodeRange = sel.ownerDocument.createRange();
28141             try {
28142                 nodeRange.selectNode(sel);
28143             } catch (e) {
28144                 nodeRange.selectNodeContents(sel);
28145             }
28146             //nodeRange.collapse(true);
28147             var s = this.editorcore.win.getSelection();
28148             s.removeAllRanges();
28149             s.addRange(nodeRange);
28150         }  
28151         
28152       
28153         var updateFooter = sel ? false : true;
28154         
28155         
28156         var ans = this.editorcore.getAllAncestors();
28157         
28158         // pick
28159         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28160         
28161         if (!sel) { 
28162             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28163             sel = sel ? sel : this.editorcore.doc.body;
28164             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28165             
28166         }
28167         // pick a menu that exists..
28168         var tn = sel.tagName.toUpperCase();
28169         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28170         
28171         tn = sel.tagName.toUpperCase();
28172         
28173         var lastSel = this.tb.selectedNode;
28174         
28175         this.tb.selectedNode = sel;
28176         
28177         // if current menu does not match..
28178         
28179         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28180                 
28181             this.tb.el.hide();
28182             ///console.log("show: " + tn);
28183             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28184             this.tb.el.show();
28185             // update name
28186             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28187             
28188             
28189             // update attributes
28190             if (this.tb.fields) {
28191                 this.tb.fields.each(function(e) {
28192                     if (e.stylename) {
28193                         e.setValue(sel.style[e.stylename]);
28194                         return;
28195                     } 
28196                    e.setValue(sel.getAttribute(e.attrname));
28197                 });
28198             }
28199             
28200             var hasStyles = false;
28201             for(var i in this.styles) {
28202                 hasStyles = true;
28203                 break;
28204             }
28205             
28206             // update styles
28207             if (hasStyles) { 
28208                 var st = this.tb.fields.item(0);
28209                 
28210                 st.store.removeAll();
28211                
28212                 
28213                 var cn = sel.className.split(/\s+/);
28214                 
28215                 var avs = [];
28216                 if (this.styles['*']) {
28217                     
28218                     Roo.each(this.styles['*'], function(v) {
28219                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28220                     });
28221                 }
28222                 if (this.styles[tn]) { 
28223                     Roo.each(this.styles[tn], function(v) {
28224                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28225                     });
28226                 }
28227                 
28228                 st.store.loadData(avs);
28229                 st.collapse();
28230                 st.setValue(cn);
28231             }
28232             // flag our selected Node.
28233             this.tb.selectedNode = sel;
28234            
28235            
28236             Roo.menu.MenuMgr.hideAll();
28237
28238         }
28239         
28240         if (!updateFooter) {
28241             //this.footDisp.dom.innerHTML = ''; 
28242             return;
28243         }
28244         // update the footer
28245         //
28246         var html = '';
28247         
28248         this.footerEls = ans.reverse();
28249         Roo.each(this.footerEls, function(a,i) {
28250             if (!a) { return; }
28251             html += html.length ? ' &gt; '  :  '';
28252             
28253             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28254             
28255         });
28256        
28257         // 
28258         var sz = this.footDisp.up('td').getSize();
28259         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28260         this.footDisp.dom.style.marginLeft = '5px';
28261         
28262         this.footDisp.dom.style.overflow = 'hidden';
28263         
28264         this.footDisp.dom.innerHTML = html;
28265             
28266         //this.editorsyncValue();
28267     },
28268      
28269     
28270    
28271        
28272     // private
28273     onDestroy : function(){
28274         if(this.rendered){
28275             
28276             this.tb.items.each(function(item){
28277                 if(item.menu){
28278                     item.menu.removeAll();
28279                     if(item.menu.el){
28280                         item.menu.el.destroy();
28281                     }
28282                 }
28283                 item.destroy();
28284             });
28285              
28286         }
28287     },
28288     onFirstFocus: function() {
28289         // need to do this for all the toolbars..
28290         this.tb.items.each(function(item){
28291            item.enable();
28292         });
28293     },
28294     buildToolbar: function(tlist, nm)
28295     {
28296         var editor = this.editor;
28297         var editorcore = this.editorcore;
28298          // create a new element.
28299         var wdiv = editor.wrap.createChild({
28300                 tag: 'div'
28301             }, editor.wrap.dom.firstChild.nextSibling, true);
28302         
28303        
28304         var tb = new Roo.Toolbar(wdiv);
28305         // add the name..
28306         
28307         tb.add(nm+ ":&nbsp;");
28308         
28309         var styles = [];
28310         for(var i in this.styles) {
28311             styles.push(i);
28312         }
28313         
28314         // styles...
28315         if (styles && styles.length) {
28316             
28317             // this needs a multi-select checkbox...
28318             tb.addField( new Roo.form.ComboBox({
28319                 store: new Roo.data.SimpleStore({
28320                     id : 'val',
28321                     fields: ['val', 'selected'],
28322                     data : [] 
28323                 }),
28324                 name : '-roo-edit-className',
28325                 attrname : 'className',
28326                 displayField: 'val',
28327                 typeAhead: false,
28328                 mode: 'local',
28329                 editable : false,
28330                 triggerAction: 'all',
28331                 emptyText:'Select Style',
28332                 selectOnFocus:true,
28333                 width: 130,
28334                 listeners : {
28335                     'select': function(c, r, i) {
28336                         // initial support only for on class per el..
28337                         tb.selectedNode.className =  r ? r.get('val') : '';
28338                         editorcore.syncValue();
28339                     }
28340                 }
28341     
28342             }));
28343         }
28344         
28345         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28346         var tbops = tbc.options;
28347         
28348         for (var i in tlist) {
28349             
28350             var item = tlist[i];
28351             tb.add(item.title + ":&nbsp;");
28352             
28353             
28354             //optname == used so you can configure the options available..
28355             var opts = item.opts ? item.opts : false;
28356             if (item.optname) {
28357                 opts = tbops[item.optname];
28358            
28359             }
28360             
28361             if (opts) {
28362                 // opts == pulldown..
28363                 tb.addField( new Roo.form.ComboBox({
28364                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28365                         id : 'val',
28366                         fields: ['val', 'display'],
28367                         data : opts  
28368                     }),
28369                     name : '-roo-edit-' + i,
28370                     attrname : i,
28371                     stylename : item.style ? item.style : false,
28372                     displayField: item.displayField ? item.displayField : 'val',
28373                     valueField :  'val',
28374                     typeAhead: false,
28375                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28376                     editable : false,
28377                     triggerAction: 'all',
28378                     emptyText:'Select',
28379                     selectOnFocus:true,
28380                     width: item.width ? item.width  : 130,
28381                     listeners : {
28382                         'select': function(c, r, i) {
28383                             if (c.stylename) {
28384                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28385                                 return;
28386                             }
28387                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28388                         }
28389                     }
28390
28391                 }));
28392                 continue;
28393                     
28394                  
28395                 
28396                 tb.addField( new Roo.form.TextField({
28397                     name: i,
28398                     width: 100,
28399                     //allowBlank:false,
28400                     value: ''
28401                 }));
28402                 continue;
28403             }
28404             tb.addField( new Roo.form.TextField({
28405                 name: '-roo-edit-' + i,
28406                 attrname : i,
28407                 
28408                 width: item.width,
28409                 //allowBlank:true,
28410                 value: '',
28411                 listeners: {
28412                     'change' : function(f, nv, ov) {
28413                         tb.selectedNode.setAttribute(f.attrname, nv);
28414                         editorcore.syncValue();
28415                     }
28416                 }
28417             }));
28418              
28419         }
28420         
28421         var _this = this;
28422         
28423         if(nm == 'BODY'){
28424             tb.addSeparator();
28425         
28426             tb.addButton( {
28427                 text: 'Stylesheets',
28428
28429                 listeners : {
28430                     click : function ()
28431                     {
28432                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28433                     }
28434                 }
28435             });
28436         }
28437         
28438         tb.addFill();
28439         tb.addButton( {
28440             text: 'Remove Tag',
28441     
28442             listeners : {
28443                 click : function ()
28444                 {
28445                     // remove
28446                     // undo does not work.
28447                      
28448                     var sn = tb.selectedNode;
28449                     
28450                     var pn = sn.parentNode;
28451                     
28452                     var stn =  sn.childNodes[0];
28453                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28454                     while (sn.childNodes.length) {
28455                         var node = sn.childNodes[0];
28456                         sn.removeChild(node);
28457                         //Roo.log(node);
28458                         pn.insertBefore(node, sn);
28459                         
28460                     }
28461                     pn.removeChild(sn);
28462                     var range = editorcore.createRange();
28463         
28464                     range.setStart(stn,0);
28465                     range.setEnd(en,0); //????
28466                     //range.selectNode(sel);
28467                     
28468                     
28469                     var selection = editorcore.getSelection();
28470                     selection.removeAllRanges();
28471                     selection.addRange(range);
28472                     
28473                     
28474                     
28475                     //_this.updateToolbar(null, null, pn);
28476                     _this.updateToolbar(null, null, null);
28477                     _this.footDisp.dom.innerHTML = ''; 
28478                 }
28479             }
28480             
28481                     
28482                 
28483             
28484         });
28485         
28486         
28487         tb.el.on('click', function(e){
28488             e.preventDefault(); // what does this do?
28489         });
28490         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28491         tb.el.hide();
28492         tb.name = nm;
28493         // dont need to disable them... as they will get hidden
28494         return tb;
28495          
28496         
28497     },
28498     buildFooter : function()
28499     {
28500         
28501         var fel = this.editor.wrap.createChild();
28502         this.footer = new Roo.Toolbar(fel);
28503         // toolbar has scrolly on left / right?
28504         var footDisp= new Roo.Toolbar.Fill();
28505         var _t = this;
28506         this.footer.add(
28507             {
28508                 text : '&lt;',
28509                 xtype: 'Button',
28510                 handler : function() {
28511                     _t.footDisp.scrollTo('left',0,true)
28512                 }
28513             }
28514         );
28515         this.footer.add( footDisp );
28516         this.footer.add( 
28517             {
28518                 text : '&gt;',
28519                 xtype: 'Button',
28520                 handler : function() {
28521                     // no animation..
28522                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28523                 }
28524             }
28525         );
28526         var fel = Roo.get(footDisp.el);
28527         fel.addClass('x-editor-context');
28528         this.footDispWrap = fel; 
28529         this.footDispWrap.overflow  = 'hidden';
28530         
28531         this.footDisp = fel.createChild();
28532         this.footDispWrap.on('click', this.onContextClick, this)
28533         
28534         
28535     },
28536     onContextClick : function (ev,dom)
28537     {
28538         ev.preventDefault();
28539         var  cn = dom.className;
28540         //Roo.log(cn);
28541         if (!cn.match(/x-ed-loc-/)) {
28542             return;
28543         }
28544         var n = cn.split('-').pop();
28545         var ans = this.footerEls;
28546         var sel = ans[n];
28547         
28548          // pick
28549         var range = this.editorcore.createRange();
28550         
28551         range.selectNodeContents(sel);
28552         //range.selectNode(sel);
28553         
28554         
28555         var selection = this.editorcore.getSelection();
28556         selection.removeAllRanges();
28557         selection.addRange(range);
28558         
28559         
28560         
28561         this.updateToolbar(null, null, sel);
28562         
28563         
28564     }
28565     
28566     
28567     
28568     
28569     
28570 });
28571
28572
28573
28574
28575
28576 /*
28577  * Based on:
28578  * Ext JS Library 1.1.1
28579  * Copyright(c) 2006-2007, Ext JS, LLC.
28580  *
28581  * Originally Released Under LGPL - original licence link has changed is not relivant.
28582  *
28583  * Fork - LGPL
28584  * <script type="text/javascript">
28585  */
28586  
28587 /**
28588  * @class Roo.form.BasicForm
28589  * @extends Roo.util.Observable
28590  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28591  * @constructor
28592  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28593  * @param {Object} config Configuration options
28594  */
28595 Roo.form.BasicForm = function(el, config){
28596     this.allItems = [];
28597     this.childForms = [];
28598     Roo.apply(this, config);
28599     /*
28600      * The Roo.form.Field items in this form.
28601      * @type MixedCollection
28602      */
28603      
28604      
28605     this.items = new Roo.util.MixedCollection(false, function(o){
28606         return o.id || (o.id = Roo.id());
28607     });
28608     this.addEvents({
28609         /**
28610          * @event beforeaction
28611          * Fires before any action is performed. Return false to cancel the action.
28612          * @param {Form} this
28613          * @param {Action} action The action to be performed
28614          */
28615         beforeaction: true,
28616         /**
28617          * @event actionfailed
28618          * Fires when an action fails.
28619          * @param {Form} this
28620          * @param {Action} action The action that failed
28621          */
28622         actionfailed : true,
28623         /**
28624          * @event actioncomplete
28625          * Fires when an action is completed.
28626          * @param {Form} this
28627          * @param {Action} action The action that completed
28628          */
28629         actioncomplete : true
28630     });
28631     if(el){
28632         this.initEl(el);
28633     }
28634     Roo.form.BasicForm.superclass.constructor.call(this);
28635 };
28636
28637 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28638     /**
28639      * @cfg {String} method
28640      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28641      */
28642     /**
28643      * @cfg {DataReader} reader
28644      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28645      * This is optional as there is built-in support for processing JSON.
28646      */
28647     /**
28648      * @cfg {DataReader} errorReader
28649      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28650      * This is completely optional as there is built-in support for processing JSON.
28651      */
28652     /**
28653      * @cfg {String} url
28654      * The URL to use for form actions if one isn't supplied in the action options.
28655      */
28656     /**
28657      * @cfg {Boolean} fileUpload
28658      * Set to true if this form is a file upload.
28659      */
28660      
28661     /**
28662      * @cfg {Object} baseParams
28663      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28664      */
28665      /**
28666      
28667     /**
28668      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28669      */
28670     timeout: 30,
28671
28672     // private
28673     activeAction : null,
28674
28675     /**
28676      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28677      * or setValues() data instead of when the form was first created.
28678      */
28679     trackResetOnLoad : false,
28680     
28681     
28682     /**
28683      * childForms - used for multi-tab forms
28684      * @type {Array}
28685      */
28686     childForms : false,
28687     
28688     /**
28689      * allItems - full list of fields.
28690      * @type {Array}
28691      */
28692     allItems : false,
28693     
28694     /**
28695      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28696      * element by passing it or its id or mask the form itself by passing in true.
28697      * @type Mixed
28698      */
28699     waitMsgTarget : false,
28700
28701     // private
28702     initEl : function(el){
28703         this.el = Roo.get(el);
28704         this.id = this.el.id || Roo.id();
28705         this.el.on('submit', this.onSubmit, this);
28706         this.el.addClass('x-form');
28707     },
28708
28709     // private
28710     onSubmit : function(e){
28711         e.stopEvent();
28712     },
28713
28714     /**
28715      * Returns true if client-side validation on the form is successful.
28716      * @return Boolean
28717      */
28718     isValid : function(){
28719         var valid = true;
28720         this.items.each(function(f){
28721            if(!f.validate()){
28722                valid = false;
28723            }
28724         });
28725         return valid;
28726     },
28727
28728     /**
28729      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
28730      * @return Boolean
28731      */
28732     isDirty : function(){
28733         var dirty = false;
28734         this.items.each(function(f){
28735            if(f.isDirty()){
28736                dirty = true;
28737                return false;
28738            }
28739         });
28740         return dirty;
28741     },
28742     
28743     /**
28744      * Returns true if any fields in this form have changed since their original load. (New version)
28745      * @return Boolean
28746      */
28747     
28748     hasChanged : function()
28749     {
28750         var dirty = false;
28751         this.items.each(function(f){
28752            if(f.hasChanged()){
28753                dirty = true;
28754                return false;
28755            }
28756         });
28757         return dirty;
28758         
28759     },
28760     /**
28761      * Resets all hasChanged to 'false' -
28762      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
28763      * So hasChanged storage is only to be used for this purpose
28764      * @return Boolean
28765      */
28766     resetHasChanged : function()
28767     {
28768         this.items.each(function(f){
28769            f.resetHasChanged();
28770         });
28771         
28772     },
28773     
28774     
28775     /**
28776      * Performs a predefined action (submit or load) or custom actions you define on this form.
28777      * @param {String} actionName The name of the action type
28778      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28779      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28780      * accept other config options):
28781      * <pre>
28782 Property          Type             Description
28783 ----------------  ---------------  ----------------------------------------------------------------------------------
28784 url               String           The url for the action (defaults to the form's url)
28785 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28786 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28787 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28788                                    validate the form on the client (defaults to false)
28789      * </pre>
28790      * @return {BasicForm} this
28791      */
28792     doAction : function(action, options){
28793         if(typeof action == 'string'){
28794             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28795         }
28796         if(this.fireEvent('beforeaction', this, action) !== false){
28797             this.beforeAction(action);
28798             action.run.defer(100, action);
28799         }
28800         return this;
28801     },
28802
28803     /**
28804      * Shortcut to do a submit action.
28805      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28806      * @return {BasicForm} this
28807      */
28808     submit : function(options){
28809         this.doAction('submit', options);
28810         return this;
28811     },
28812
28813     /**
28814      * Shortcut to do a load action.
28815      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28816      * @return {BasicForm} this
28817      */
28818     load : function(options){
28819         this.doAction('load', options);
28820         return this;
28821     },
28822
28823     /**
28824      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28825      * @param {Record} record The record to edit
28826      * @return {BasicForm} this
28827      */
28828     updateRecord : function(record){
28829         record.beginEdit();
28830         var fs = record.fields;
28831         fs.each(function(f){
28832             var field = this.findField(f.name);
28833             if(field){
28834                 record.set(f.name, field.getValue());
28835             }
28836         }, this);
28837         record.endEdit();
28838         return this;
28839     },
28840
28841     /**
28842      * Loads an Roo.data.Record into this form.
28843      * @param {Record} record The record to load
28844      * @return {BasicForm} this
28845      */
28846     loadRecord : function(record){
28847         this.setValues(record.data);
28848         return this;
28849     },
28850
28851     // private
28852     beforeAction : function(action){
28853         var o = action.options;
28854         
28855        
28856         if(this.waitMsgTarget === true){
28857             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28858         }else if(this.waitMsgTarget){
28859             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28860             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28861         }else {
28862             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28863         }
28864          
28865     },
28866
28867     // private
28868     afterAction : function(action, success){
28869         this.activeAction = null;
28870         var o = action.options;
28871         
28872         if(this.waitMsgTarget === true){
28873             this.el.unmask();
28874         }else if(this.waitMsgTarget){
28875             this.waitMsgTarget.unmask();
28876         }else{
28877             Roo.MessageBox.updateProgress(1);
28878             Roo.MessageBox.hide();
28879         }
28880          
28881         if(success){
28882             if(o.reset){
28883                 this.reset();
28884             }
28885             Roo.callback(o.success, o.scope, [this, action]);
28886             this.fireEvent('actioncomplete', this, action);
28887             
28888         }else{
28889             
28890             // failure condition..
28891             // we have a scenario where updates need confirming.
28892             // eg. if a locking scenario exists..
28893             // we look for { errors : { needs_confirm : true }} in the response.
28894             if (
28895                 (typeof(action.result) != 'undefined')  &&
28896                 (typeof(action.result.errors) != 'undefined')  &&
28897                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28898            ){
28899                 var _t = this;
28900                 Roo.MessageBox.confirm(
28901                     "Change requires confirmation",
28902                     action.result.errorMsg,
28903                     function(r) {
28904                         if (r != 'yes') {
28905                             return;
28906                         }
28907                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28908                     }
28909                     
28910                 );
28911                 
28912                 
28913                 
28914                 return;
28915             }
28916             
28917             Roo.callback(o.failure, o.scope, [this, action]);
28918             // show an error message if no failed handler is set..
28919             if (!this.hasListener('actionfailed')) {
28920                 Roo.MessageBox.alert("Error",
28921                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28922                         action.result.errorMsg :
28923                         "Saving Failed, please check your entries or try again"
28924                 );
28925             }
28926             
28927             this.fireEvent('actionfailed', this, action);
28928         }
28929         
28930     },
28931
28932     /**
28933      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28934      * @param {String} id The value to search for
28935      * @return Field
28936      */
28937     findField : function(id){
28938         var field = this.items.get(id);
28939         if(!field){
28940             this.items.each(function(f){
28941                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28942                     field = f;
28943                     return false;
28944                 }
28945             });
28946         }
28947         return field || null;
28948     },
28949
28950     /**
28951      * Add a secondary form to this one, 
28952      * Used to provide tabbed forms. One form is primary, with hidden values 
28953      * which mirror the elements from the other forms.
28954      * 
28955      * @param {Roo.form.Form} form to add.
28956      * 
28957      */
28958     addForm : function(form)
28959     {
28960        
28961         if (this.childForms.indexOf(form) > -1) {
28962             // already added..
28963             return;
28964         }
28965         this.childForms.push(form);
28966         var n = '';
28967         Roo.each(form.allItems, function (fe) {
28968             
28969             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28970             if (this.findField(n)) { // already added..
28971                 return;
28972             }
28973             var add = new Roo.form.Hidden({
28974                 name : n
28975             });
28976             add.render(this.el);
28977             
28978             this.add( add );
28979         }, this);
28980         
28981     },
28982     /**
28983      * Mark fields in this form invalid in bulk.
28984      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28985      * @return {BasicForm} this
28986      */
28987     markInvalid : function(errors){
28988         if(errors instanceof Array){
28989             for(var i = 0, len = errors.length; i < len; i++){
28990                 var fieldError = errors[i];
28991                 var f = this.findField(fieldError.id);
28992                 if(f){
28993                     f.markInvalid(fieldError.msg);
28994                 }
28995             }
28996         }else{
28997             var field, id;
28998             for(id in errors){
28999                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29000                     field.markInvalid(errors[id]);
29001                 }
29002             }
29003         }
29004         Roo.each(this.childForms || [], function (f) {
29005             f.markInvalid(errors);
29006         });
29007         
29008         return this;
29009     },
29010
29011     /**
29012      * Set values for fields in this form in bulk.
29013      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29014      * @return {BasicForm} this
29015      */
29016     setValues : function(values){
29017         if(values instanceof Array){ // array of objects
29018             for(var i = 0, len = values.length; i < len; i++){
29019                 var v = values[i];
29020                 var f = this.findField(v.id);
29021                 if(f){
29022                     f.setValue(v.value);
29023                     if(this.trackResetOnLoad){
29024                         f.originalValue = f.getValue();
29025                     }
29026                 }
29027             }
29028         }else{ // object hash
29029             var field, id;
29030             for(id in values){
29031                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29032                     
29033                     if (field.setFromData && 
29034                         field.valueField && 
29035                         field.displayField &&
29036                         // combos' with local stores can 
29037                         // be queried via setValue()
29038                         // to set their value..
29039                         (field.store && !field.store.isLocal)
29040                         ) {
29041                         // it's a combo
29042                         var sd = { };
29043                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29044                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29045                         field.setFromData(sd);
29046                         
29047                     } else {
29048                         field.setValue(values[id]);
29049                     }
29050                     
29051                     
29052                     if(this.trackResetOnLoad){
29053                         field.originalValue = field.getValue();
29054                     }
29055                 }
29056             }
29057         }
29058         this.resetHasChanged();
29059         
29060         
29061         Roo.each(this.childForms || [], function (f) {
29062             f.setValues(values);
29063             f.resetHasChanged();
29064         });
29065                 
29066         return this;
29067     },
29068
29069     /**
29070      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29071      * they are returned as an array.
29072      * @param {Boolean} asString
29073      * @return {Object}
29074      */
29075     getValues : function(asString){
29076         if (this.childForms) {
29077             // copy values from the child forms
29078             Roo.each(this.childForms, function (f) {
29079                 this.setValues(f.getValues());
29080             }, this);
29081         }
29082         
29083         
29084         
29085         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29086         if(asString === true){
29087             return fs;
29088         }
29089         return Roo.urlDecode(fs);
29090     },
29091     
29092     /**
29093      * Returns the fields in this form as an object with key/value pairs. 
29094      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29095      * @return {Object}
29096      */
29097     getFieldValues : function(with_hidden)
29098     {
29099         if (this.childForms) {
29100             // copy values from the child forms
29101             // should this call getFieldValues - probably not as we do not currently copy
29102             // hidden fields when we generate..
29103             Roo.each(this.childForms, function (f) {
29104                 this.setValues(f.getValues());
29105             }, this);
29106         }
29107         
29108         var ret = {};
29109         this.items.each(function(f){
29110             if (!f.getName()) {
29111                 return;
29112             }
29113             var v = f.getValue();
29114             if (f.inputType =='radio') {
29115                 if (typeof(ret[f.getName()]) == 'undefined') {
29116                     ret[f.getName()] = ''; // empty..
29117                 }
29118                 
29119                 if (!f.el.dom.checked) {
29120                     return;
29121                     
29122                 }
29123                 v = f.el.dom.value;
29124                 
29125             }
29126             
29127             // not sure if this supported any more..
29128             if ((typeof(v) == 'object') && f.getRawValue) {
29129                 v = f.getRawValue() ; // dates..
29130             }
29131             // combo boxes where name != hiddenName...
29132             if (f.name != f.getName()) {
29133                 ret[f.name] = f.getRawValue();
29134             }
29135             ret[f.getName()] = v;
29136         });
29137         
29138         return ret;
29139     },
29140
29141     /**
29142      * Clears all invalid messages in this form.
29143      * @return {BasicForm} this
29144      */
29145     clearInvalid : function(){
29146         this.items.each(function(f){
29147            f.clearInvalid();
29148         });
29149         
29150         Roo.each(this.childForms || [], function (f) {
29151             f.clearInvalid();
29152         });
29153         
29154         
29155         return this;
29156     },
29157
29158     /**
29159      * Resets this form.
29160      * @return {BasicForm} this
29161      */
29162     reset : function(){
29163         this.items.each(function(f){
29164             f.reset();
29165         });
29166         
29167         Roo.each(this.childForms || [], function (f) {
29168             f.reset();
29169         });
29170         this.resetHasChanged();
29171         
29172         return this;
29173     },
29174
29175     /**
29176      * Add Roo.form components to this form.
29177      * @param {Field} field1
29178      * @param {Field} field2 (optional)
29179      * @param {Field} etc (optional)
29180      * @return {BasicForm} this
29181      */
29182     add : function(){
29183         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29184         return this;
29185     },
29186
29187
29188     /**
29189      * Removes a field from the items collection (does NOT remove its markup).
29190      * @param {Field} field
29191      * @return {BasicForm} this
29192      */
29193     remove : function(field){
29194         this.items.remove(field);
29195         return this;
29196     },
29197
29198     /**
29199      * Looks at the fields in this form, checks them for an id attribute,
29200      * and calls applyTo on the existing dom element with that id.
29201      * @return {BasicForm} this
29202      */
29203     render : function(){
29204         this.items.each(function(f){
29205             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29206                 f.applyTo(f.id);
29207             }
29208         });
29209         return this;
29210     },
29211
29212     /**
29213      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29214      * @param {Object} values
29215      * @return {BasicForm} this
29216      */
29217     applyToFields : function(o){
29218         this.items.each(function(f){
29219            Roo.apply(f, o);
29220         });
29221         return this;
29222     },
29223
29224     /**
29225      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29226      * @param {Object} values
29227      * @return {BasicForm} this
29228      */
29229     applyIfToFields : function(o){
29230         this.items.each(function(f){
29231            Roo.applyIf(f, o);
29232         });
29233         return this;
29234     }
29235 });
29236
29237 // back compat
29238 Roo.BasicForm = Roo.form.BasicForm;/*
29239  * Based on:
29240  * Ext JS Library 1.1.1
29241  * Copyright(c) 2006-2007, Ext JS, LLC.
29242  *
29243  * Originally Released Under LGPL - original licence link has changed is not relivant.
29244  *
29245  * Fork - LGPL
29246  * <script type="text/javascript">
29247  */
29248
29249 /**
29250  * @class Roo.form.Form
29251  * @extends Roo.form.BasicForm
29252  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29253  * @constructor
29254  * @param {Object} config Configuration options
29255  */
29256 Roo.form.Form = function(config){
29257     var xitems =  [];
29258     if (config.items) {
29259         xitems = config.items;
29260         delete config.items;
29261     }
29262    
29263     
29264     Roo.form.Form.superclass.constructor.call(this, null, config);
29265     this.url = this.url || this.action;
29266     if(!this.root){
29267         this.root = new Roo.form.Layout(Roo.applyIf({
29268             id: Roo.id()
29269         }, config));
29270     }
29271     this.active = this.root;
29272     /**
29273      * Array of all the buttons that have been added to this form via {@link addButton}
29274      * @type Array
29275      */
29276     this.buttons = [];
29277     this.allItems = [];
29278     this.addEvents({
29279         /**
29280          * @event clientvalidation
29281          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29282          * @param {Form} this
29283          * @param {Boolean} valid true if the form has passed client-side validation
29284          */
29285         clientvalidation: true,
29286         /**
29287          * @event rendered
29288          * Fires when the form is rendered
29289          * @param {Roo.form.Form} form
29290          */
29291         rendered : true
29292     });
29293     
29294     if (this.progressUrl) {
29295             // push a hidden field onto the list of fields..
29296             this.addxtype( {
29297                     xns: Roo.form, 
29298                     xtype : 'Hidden', 
29299                     name : 'UPLOAD_IDENTIFIER' 
29300             });
29301         }
29302         
29303     
29304     Roo.each(xitems, this.addxtype, this);
29305     
29306     
29307     
29308 };
29309
29310 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29311     /**
29312      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29313      */
29314     /**
29315      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29316      */
29317     /**
29318      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29319      */
29320     buttonAlign:'center',
29321
29322     /**
29323      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29324      */
29325     minButtonWidth:75,
29326
29327     /**
29328      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29329      * This property cascades to child containers if not set.
29330      */
29331     labelAlign:'left',
29332
29333     /**
29334      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29335      * fires a looping event with that state. This is required to bind buttons to the valid
29336      * state using the config value formBind:true on the button.
29337      */
29338     monitorValid : false,
29339
29340     /**
29341      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29342      */
29343     monitorPoll : 200,
29344     
29345     /**
29346      * @cfg {String} progressUrl - Url to return progress data 
29347      */
29348     
29349     progressUrl : false,
29350   
29351     /**
29352      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29353      * fields are added and the column is closed. If no fields are passed the column remains open
29354      * until end() is called.
29355      * @param {Object} config The config to pass to the column
29356      * @param {Field} field1 (optional)
29357      * @param {Field} field2 (optional)
29358      * @param {Field} etc (optional)
29359      * @return Column The column container object
29360      */
29361     column : function(c){
29362         var col = new Roo.form.Column(c);
29363         this.start(col);
29364         if(arguments.length > 1){ // duplicate code required because of Opera
29365             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29366             this.end();
29367         }
29368         return col;
29369     },
29370
29371     /**
29372      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29373      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29374      * until end() is called.
29375      * @param {Object} config The config to pass to the fieldset
29376      * @param {Field} field1 (optional)
29377      * @param {Field} field2 (optional)
29378      * @param {Field} etc (optional)
29379      * @return FieldSet The fieldset container object
29380      */
29381     fieldset : function(c){
29382         var fs = new Roo.form.FieldSet(c);
29383         this.start(fs);
29384         if(arguments.length > 1){ // duplicate code required because of Opera
29385             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29386             this.end();
29387         }
29388         return fs;
29389     },
29390
29391     /**
29392      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29393      * fields are added and the container is closed. If no fields are passed the container remains open
29394      * until end() is called.
29395      * @param {Object} config The config to pass to the Layout
29396      * @param {Field} field1 (optional)
29397      * @param {Field} field2 (optional)
29398      * @param {Field} etc (optional)
29399      * @return Layout The container object
29400      */
29401     container : function(c){
29402         var l = new Roo.form.Layout(c);
29403         this.start(l);
29404         if(arguments.length > 1){ // duplicate code required because of Opera
29405             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29406             this.end();
29407         }
29408         return l;
29409     },
29410
29411     /**
29412      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29413      * @param {Object} container A Roo.form.Layout or subclass of Layout
29414      * @return {Form} this
29415      */
29416     start : function(c){
29417         // cascade label info
29418         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29419         this.active.stack.push(c);
29420         c.ownerCt = this.active;
29421         this.active = c;
29422         return this;
29423     },
29424
29425     /**
29426      * Closes the current open container
29427      * @return {Form} this
29428      */
29429     end : function(){
29430         if(this.active == this.root){
29431             return this;
29432         }
29433         this.active = this.active.ownerCt;
29434         return this;
29435     },
29436
29437     /**
29438      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29439      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29440      * as the label of the field.
29441      * @param {Field} field1
29442      * @param {Field} field2 (optional)
29443      * @param {Field} etc. (optional)
29444      * @return {Form} this
29445      */
29446     add : function(){
29447         this.active.stack.push.apply(this.active.stack, arguments);
29448         this.allItems.push.apply(this.allItems,arguments);
29449         var r = [];
29450         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29451             if(a[i].isFormField){
29452                 r.push(a[i]);
29453             }
29454         }
29455         if(r.length > 0){
29456             Roo.form.Form.superclass.add.apply(this, r);
29457         }
29458         return this;
29459     },
29460     
29461
29462     
29463     
29464     
29465      /**
29466      * Find any element that has been added to a form, using it's ID or name
29467      * This can include framesets, columns etc. along with regular fields..
29468      * @param {String} id - id or name to find.
29469      
29470      * @return {Element} e - or false if nothing found.
29471      */
29472     findbyId : function(id)
29473     {
29474         var ret = false;
29475         if (!id) {
29476             return ret;
29477         }
29478         Roo.each(this.allItems, function(f){
29479             if (f.id == id || f.name == id ){
29480                 ret = f;
29481                 return false;
29482             }
29483         });
29484         return ret;
29485     },
29486
29487     
29488     
29489     /**
29490      * Render this form into the passed container. This should only be called once!
29491      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29492      * @return {Form} this
29493      */
29494     render : function(ct)
29495     {
29496         
29497         
29498         
29499         ct = Roo.get(ct);
29500         var o = this.autoCreate || {
29501             tag: 'form',
29502             method : this.method || 'POST',
29503             id : this.id || Roo.id()
29504         };
29505         this.initEl(ct.createChild(o));
29506
29507         this.root.render(this.el);
29508         
29509        
29510              
29511         this.items.each(function(f){
29512             f.render('x-form-el-'+f.id);
29513         });
29514
29515         if(this.buttons.length > 0){
29516             // tables are required to maintain order and for correct IE layout
29517             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29518                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29519                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29520             }}, null, true);
29521             var tr = tb.getElementsByTagName('tr')[0];
29522             for(var i = 0, len = this.buttons.length; i < len; i++) {
29523                 var b = this.buttons[i];
29524                 var td = document.createElement('td');
29525                 td.className = 'x-form-btn-td';
29526                 b.render(tr.appendChild(td));
29527             }
29528         }
29529         if(this.monitorValid){ // initialize after render
29530             this.startMonitoring();
29531         }
29532         this.fireEvent('rendered', this);
29533         return this;
29534     },
29535
29536     /**
29537      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29538      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29539      * object or a valid Roo.DomHelper element config
29540      * @param {Function} handler The function called when the button is clicked
29541      * @param {Object} scope (optional) The scope of the handler function
29542      * @return {Roo.Button}
29543      */
29544     addButton : function(config, handler, scope){
29545         var bc = {
29546             handler: handler,
29547             scope: scope,
29548             minWidth: this.minButtonWidth,
29549             hideParent:true
29550         };
29551         if(typeof config == "string"){
29552             bc.text = config;
29553         }else{
29554             Roo.apply(bc, config);
29555         }
29556         var btn = new Roo.Button(null, bc);
29557         this.buttons.push(btn);
29558         return btn;
29559     },
29560
29561      /**
29562      * Adds a series of form elements (using the xtype property as the factory method.
29563      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29564      * @param {Object} config 
29565      */
29566     
29567     addxtype : function()
29568     {
29569         var ar = Array.prototype.slice.call(arguments, 0);
29570         var ret = false;
29571         for(var i = 0; i < ar.length; i++) {
29572             if (!ar[i]) {
29573                 continue; // skip -- if this happends something invalid got sent, we 
29574                 // should ignore it, as basically that interface element will not show up
29575                 // and that should be pretty obvious!!
29576             }
29577             
29578             if (Roo.form[ar[i].xtype]) {
29579                 ar[i].form = this;
29580                 var fe = Roo.factory(ar[i], Roo.form);
29581                 if (!ret) {
29582                     ret = fe;
29583                 }
29584                 fe.form = this;
29585                 if (fe.store) {
29586                     fe.store.form = this;
29587                 }
29588                 if (fe.isLayout) {  
29589                          
29590                     this.start(fe);
29591                     this.allItems.push(fe);
29592                     if (fe.items && fe.addxtype) {
29593                         fe.addxtype.apply(fe, fe.items);
29594                         delete fe.items;
29595                     }
29596                      this.end();
29597                     continue;
29598                 }
29599                 
29600                 
29601                  
29602                 this.add(fe);
29603               //  console.log('adding ' + ar[i].xtype);
29604             }
29605             if (ar[i].xtype == 'Button') {  
29606                 //console.log('adding button');
29607                 //console.log(ar[i]);
29608                 this.addButton(ar[i]);
29609                 this.allItems.push(fe);
29610                 continue;
29611             }
29612             
29613             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29614                 alert('end is not supported on xtype any more, use items');
29615             //    this.end();
29616             //    //console.log('adding end');
29617             }
29618             
29619         }
29620         return ret;
29621     },
29622     
29623     /**
29624      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29625      * option "monitorValid"
29626      */
29627     startMonitoring : function(){
29628         if(!this.bound){
29629             this.bound = true;
29630             Roo.TaskMgr.start({
29631                 run : this.bindHandler,
29632                 interval : this.monitorPoll || 200,
29633                 scope: this
29634             });
29635         }
29636     },
29637
29638     /**
29639      * Stops monitoring of the valid state of this form
29640      */
29641     stopMonitoring : function(){
29642         this.bound = false;
29643     },
29644
29645     // private
29646     bindHandler : function(){
29647         if(!this.bound){
29648             return false; // stops binding
29649         }
29650         var valid = true;
29651         this.items.each(function(f){
29652             if(!f.isValid(true)){
29653                 valid = false;
29654                 return false;
29655             }
29656         });
29657         for(var i = 0, len = this.buttons.length; i < len; i++){
29658             var btn = this.buttons[i];
29659             if(btn.formBind === true && btn.disabled === valid){
29660                 btn.setDisabled(!valid);
29661             }
29662         }
29663         this.fireEvent('clientvalidation', this, valid);
29664     }
29665     
29666     
29667     
29668     
29669     
29670     
29671     
29672     
29673 });
29674
29675
29676 // back compat
29677 Roo.Form = Roo.form.Form;
29678 /*
29679  * Based on:
29680  * Ext JS Library 1.1.1
29681  * Copyright(c) 2006-2007, Ext JS, LLC.
29682  *
29683  * Originally Released Under LGPL - original licence link has changed is not relivant.
29684  *
29685  * Fork - LGPL
29686  * <script type="text/javascript">
29687  */
29688
29689 // as we use this in bootstrap.
29690 Roo.namespace('Roo.form');
29691  /**
29692  * @class Roo.form.Action
29693  * Internal Class used to handle form actions
29694  * @constructor
29695  * @param {Roo.form.BasicForm} el The form element or its id
29696  * @param {Object} config Configuration options
29697  */
29698
29699  
29700  
29701 // define the action interface
29702 Roo.form.Action = function(form, options){
29703     this.form = form;
29704     this.options = options || {};
29705 };
29706 /**
29707  * Client Validation Failed
29708  * @const 
29709  */
29710 Roo.form.Action.CLIENT_INVALID = 'client';
29711 /**
29712  * Server Validation Failed
29713  * @const 
29714  */
29715 Roo.form.Action.SERVER_INVALID = 'server';
29716  /**
29717  * Connect to Server Failed
29718  * @const 
29719  */
29720 Roo.form.Action.CONNECT_FAILURE = 'connect';
29721 /**
29722  * Reading Data from Server Failed
29723  * @const 
29724  */
29725 Roo.form.Action.LOAD_FAILURE = 'load';
29726
29727 Roo.form.Action.prototype = {
29728     type : 'default',
29729     failureType : undefined,
29730     response : undefined,
29731     result : undefined,
29732
29733     // interface method
29734     run : function(options){
29735
29736     },
29737
29738     // interface method
29739     success : function(response){
29740
29741     },
29742
29743     // interface method
29744     handleResponse : function(response){
29745
29746     },
29747
29748     // default connection failure
29749     failure : function(response){
29750         
29751         this.response = response;
29752         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29753         this.form.afterAction(this, false);
29754     },
29755
29756     processResponse : function(response){
29757         this.response = response;
29758         if(!response.responseText){
29759             return true;
29760         }
29761         this.result = this.handleResponse(response);
29762         return this.result;
29763     },
29764
29765     // utility functions used internally
29766     getUrl : function(appendParams){
29767         var url = this.options.url || this.form.url || this.form.el.dom.action;
29768         if(appendParams){
29769             var p = this.getParams();
29770             if(p){
29771                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29772             }
29773         }
29774         return url;
29775     },
29776
29777     getMethod : function(){
29778         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29779     },
29780
29781     getParams : function(){
29782         var bp = this.form.baseParams;
29783         var p = this.options.params;
29784         if(p){
29785             if(typeof p == "object"){
29786                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29787             }else if(typeof p == 'string' && bp){
29788                 p += '&' + Roo.urlEncode(bp);
29789             }
29790         }else if(bp){
29791             p = Roo.urlEncode(bp);
29792         }
29793         return p;
29794     },
29795
29796     createCallback : function(){
29797         return {
29798             success: this.success,
29799             failure: this.failure,
29800             scope: this,
29801             timeout: (this.form.timeout*1000),
29802             upload: this.form.fileUpload ? this.success : undefined
29803         };
29804     }
29805 };
29806
29807 Roo.form.Action.Submit = function(form, options){
29808     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29809 };
29810
29811 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29812     type : 'submit',
29813
29814     haveProgress : false,
29815     uploadComplete : false,
29816     
29817     // uploadProgress indicator.
29818     uploadProgress : function()
29819     {
29820         if (!this.form.progressUrl) {
29821             return;
29822         }
29823         
29824         if (!this.haveProgress) {
29825             Roo.MessageBox.progress("Uploading", "Uploading");
29826         }
29827         if (this.uploadComplete) {
29828            Roo.MessageBox.hide();
29829            return;
29830         }
29831         
29832         this.haveProgress = true;
29833    
29834         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29835         
29836         var c = new Roo.data.Connection();
29837         c.request({
29838             url : this.form.progressUrl,
29839             params: {
29840                 id : uid
29841             },
29842             method: 'GET',
29843             success : function(req){
29844                //console.log(data);
29845                 var rdata = false;
29846                 var edata;
29847                 try  {
29848                    rdata = Roo.decode(req.responseText)
29849                 } catch (e) {
29850                     Roo.log("Invalid data from server..");
29851                     Roo.log(edata);
29852                     return;
29853                 }
29854                 if (!rdata || !rdata.success) {
29855                     Roo.log(rdata);
29856                     Roo.MessageBox.alert(Roo.encode(rdata));
29857                     return;
29858                 }
29859                 var data = rdata.data;
29860                 
29861                 if (this.uploadComplete) {
29862                    Roo.MessageBox.hide();
29863                    return;
29864                 }
29865                    
29866                 if (data){
29867                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29868                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29869                     );
29870                 }
29871                 this.uploadProgress.defer(2000,this);
29872             },
29873        
29874             failure: function(data) {
29875                 Roo.log('progress url failed ');
29876                 Roo.log(data);
29877             },
29878             scope : this
29879         });
29880            
29881     },
29882     
29883     
29884     run : function()
29885     {
29886         // run get Values on the form, so it syncs any secondary forms.
29887         this.form.getValues();
29888         
29889         var o = this.options;
29890         var method = this.getMethod();
29891         var isPost = method == 'POST';
29892         if(o.clientValidation === false || this.form.isValid()){
29893             
29894             if (this.form.progressUrl) {
29895                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29896                     (new Date() * 1) + '' + Math.random());
29897                     
29898             } 
29899             
29900             
29901             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29902                 form:this.form.el.dom,
29903                 url:this.getUrl(!isPost),
29904                 method: method,
29905                 params:isPost ? this.getParams() : null,
29906                 isUpload: this.form.fileUpload
29907             }));
29908             
29909             this.uploadProgress();
29910
29911         }else if (o.clientValidation !== false){ // client validation failed
29912             this.failureType = Roo.form.Action.CLIENT_INVALID;
29913             this.form.afterAction(this, false);
29914         }
29915     },
29916
29917     success : function(response)
29918     {
29919         this.uploadComplete= true;
29920         if (this.haveProgress) {
29921             Roo.MessageBox.hide();
29922         }
29923         
29924         
29925         var result = this.processResponse(response);
29926         if(result === true || result.success){
29927             this.form.afterAction(this, true);
29928             return;
29929         }
29930         if(result.errors){
29931             this.form.markInvalid(result.errors);
29932             this.failureType = Roo.form.Action.SERVER_INVALID;
29933         }
29934         this.form.afterAction(this, false);
29935     },
29936     failure : function(response)
29937     {
29938         this.uploadComplete= true;
29939         if (this.haveProgress) {
29940             Roo.MessageBox.hide();
29941         }
29942         
29943         this.response = response;
29944         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29945         this.form.afterAction(this, false);
29946     },
29947     
29948     handleResponse : function(response){
29949         if(this.form.errorReader){
29950             var rs = this.form.errorReader.read(response);
29951             var errors = [];
29952             if(rs.records){
29953                 for(var i = 0, len = rs.records.length; i < len; i++) {
29954                     var r = rs.records[i];
29955                     errors[i] = r.data;
29956                 }
29957             }
29958             if(errors.length < 1){
29959                 errors = null;
29960             }
29961             return {
29962                 success : rs.success,
29963                 errors : errors
29964             };
29965         }
29966         var ret = false;
29967         try {
29968             ret = Roo.decode(response.responseText);
29969         } catch (e) {
29970             ret = {
29971                 success: false,
29972                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29973                 errors : []
29974             };
29975         }
29976         return ret;
29977         
29978     }
29979 });
29980
29981
29982 Roo.form.Action.Load = function(form, options){
29983     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29984     this.reader = this.form.reader;
29985 };
29986
29987 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29988     type : 'load',
29989
29990     run : function(){
29991         
29992         Roo.Ajax.request(Roo.apply(
29993                 this.createCallback(), {
29994                     method:this.getMethod(),
29995                     url:this.getUrl(false),
29996                     params:this.getParams()
29997         }));
29998     },
29999
30000     success : function(response){
30001         
30002         var result = this.processResponse(response);
30003         if(result === true || !result.success || !result.data){
30004             this.failureType = Roo.form.Action.LOAD_FAILURE;
30005             this.form.afterAction(this, false);
30006             return;
30007         }
30008         this.form.clearInvalid();
30009         this.form.setValues(result.data);
30010         this.form.afterAction(this, true);
30011     },
30012
30013     handleResponse : function(response){
30014         if(this.form.reader){
30015             var rs = this.form.reader.read(response);
30016             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30017             return {
30018                 success : rs.success,
30019                 data : data
30020             };
30021         }
30022         return Roo.decode(response.responseText);
30023     }
30024 });
30025
30026 Roo.form.Action.ACTION_TYPES = {
30027     'load' : Roo.form.Action.Load,
30028     'submit' : Roo.form.Action.Submit
30029 };/*
30030  * Based on:
30031  * Ext JS Library 1.1.1
30032  * Copyright(c) 2006-2007, Ext JS, LLC.
30033  *
30034  * Originally Released Under LGPL - original licence link has changed is not relivant.
30035  *
30036  * Fork - LGPL
30037  * <script type="text/javascript">
30038  */
30039  
30040 /**
30041  * @class Roo.form.Layout
30042  * @extends Roo.Component
30043  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30044  * @constructor
30045  * @param {Object} config Configuration options
30046  */
30047 Roo.form.Layout = function(config){
30048     var xitems = [];
30049     if (config.items) {
30050         xitems = config.items;
30051         delete config.items;
30052     }
30053     Roo.form.Layout.superclass.constructor.call(this, config);
30054     this.stack = [];
30055     Roo.each(xitems, this.addxtype, this);
30056      
30057 };
30058
30059 Roo.extend(Roo.form.Layout, Roo.Component, {
30060     /**
30061      * @cfg {String/Object} autoCreate
30062      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30063      */
30064     /**
30065      * @cfg {String/Object/Function} style
30066      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30067      * a function which returns such a specification.
30068      */
30069     /**
30070      * @cfg {String} labelAlign
30071      * Valid values are "left," "top" and "right" (defaults to "left")
30072      */
30073     /**
30074      * @cfg {Number} labelWidth
30075      * Fixed width in pixels of all field labels (defaults to undefined)
30076      */
30077     /**
30078      * @cfg {Boolean} clear
30079      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30080      */
30081     clear : true,
30082     /**
30083      * @cfg {String} labelSeparator
30084      * The separator to use after field labels (defaults to ':')
30085      */
30086     labelSeparator : ':',
30087     /**
30088      * @cfg {Boolean} hideLabels
30089      * True to suppress the display of field labels in this layout (defaults to false)
30090      */
30091     hideLabels : false,
30092
30093     // private
30094     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30095     
30096     isLayout : true,
30097     
30098     // private
30099     onRender : function(ct, position){
30100         if(this.el){ // from markup
30101             this.el = Roo.get(this.el);
30102         }else {  // generate
30103             var cfg = this.getAutoCreate();
30104             this.el = ct.createChild(cfg, position);
30105         }
30106         if(this.style){
30107             this.el.applyStyles(this.style);
30108         }
30109         if(this.labelAlign){
30110             this.el.addClass('x-form-label-'+this.labelAlign);
30111         }
30112         if(this.hideLabels){
30113             this.labelStyle = "display:none";
30114             this.elementStyle = "padding-left:0;";
30115         }else{
30116             if(typeof this.labelWidth == 'number'){
30117                 this.labelStyle = "width:"+this.labelWidth+"px;";
30118                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30119             }
30120             if(this.labelAlign == 'top'){
30121                 this.labelStyle = "width:auto;";
30122                 this.elementStyle = "padding-left:0;";
30123             }
30124         }
30125         var stack = this.stack;
30126         var slen = stack.length;
30127         if(slen > 0){
30128             if(!this.fieldTpl){
30129                 var t = new Roo.Template(
30130                     '<div class="x-form-item {5}">',
30131                         '<label for="{0}" style="{2}">{1}{4}</label>',
30132                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30133                         '</div>',
30134                     '</div><div class="x-form-clear-left"></div>'
30135                 );
30136                 t.disableFormats = true;
30137                 t.compile();
30138                 Roo.form.Layout.prototype.fieldTpl = t;
30139             }
30140             for(var i = 0; i < slen; i++) {
30141                 if(stack[i].isFormField){
30142                     this.renderField(stack[i]);
30143                 }else{
30144                     this.renderComponent(stack[i]);
30145                 }
30146             }
30147         }
30148         if(this.clear){
30149             this.el.createChild({cls:'x-form-clear'});
30150         }
30151     },
30152
30153     // private
30154     renderField : function(f){
30155         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30156                f.id, //0
30157                f.fieldLabel, //1
30158                f.labelStyle||this.labelStyle||'', //2
30159                this.elementStyle||'', //3
30160                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30161                f.itemCls||this.itemCls||''  //5
30162        ], true).getPrevSibling());
30163     },
30164
30165     // private
30166     renderComponent : function(c){
30167         c.render(c.isLayout ? this.el : this.el.createChild());    
30168     },
30169     /**
30170      * Adds a object form elements (using the xtype property as the factory method.)
30171      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30172      * @param {Object} config 
30173      */
30174     addxtype : function(o)
30175     {
30176         // create the lement.
30177         o.form = this.form;
30178         var fe = Roo.factory(o, Roo.form);
30179         this.form.allItems.push(fe);
30180         this.stack.push(fe);
30181         
30182         if (fe.isFormField) {
30183             this.form.items.add(fe);
30184         }
30185          
30186         return fe;
30187     }
30188 });
30189
30190 /**
30191  * @class Roo.form.Column
30192  * @extends Roo.form.Layout
30193  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30194  * @constructor
30195  * @param {Object} config Configuration options
30196  */
30197 Roo.form.Column = function(config){
30198     Roo.form.Column.superclass.constructor.call(this, config);
30199 };
30200
30201 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30202     /**
30203      * @cfg {Number/String} width
30204      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30205      */
30206     /**
30207      * @cfg {String/Object} autoCreate
30208      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30209      */
30210
30211     // private
30212     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30213
30214     // private
30215     onRender : function(ct, position){
30216         Roo.form.Column.superclass.onRender.call(this, ct, position);
30217         if(this.width){
30218             this.el.setWidth(this.width);
30219         }
30220     }
30221 });
30222
30223
30224 /**
30225  * @class Roo.form.Row
30226  * @extends Roo.form.Layout
30227  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30228  * @constructor
30229  * @param {Object} config Configuration options
30230  */
30231
30232  
30233 Roo.form.Row = function(config){
30234     Roo.form.Row.superclass.constructor.call(this, config);
30235 };
30236  
30237 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30238       /**
30239      * @cfg {Number/String} width
30240      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30241      */
30242     /**
30243      * @cfg {Number/String} height
30244      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30245      */
30246     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30247     
30248     padWidth : 20,
30249     // private
30250     onRender : function(ct, position){
30251         //console.log('row render');
30252         if(!this.rowTpl){
30253             var t = new Roo.Template(
30254                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30255                     '<label for="{0}" style="{2}">{1}{4}</label>',
30256                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30257                     '</div>',
30258                 '</div>'
30259             );
30260             t.disableFormats = true;
30261             t.compile();
30262             Roo.form.Layout.prototype.rowTpl = t;
30263         }
30264         this.fieldTpl = this.rowTpl;
30265         
30266         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30267         var labelWidth = 100;
30268         
30269         if ((this.labelAlign != 'top')) {
30270             if (typeof this.labelWidth == 'number') {
30271                 labelWidth = this.labelWidth
30272             }
30273             this.padWidth =  20 + labelWidth;
30274             
30275         }
30276         
30277         Roo.form.Column.superclass.onRender.call(this, ct, position);
30278         if(this.width){
30279             this.el.setWidth(this.width);
30280         }
30281         if(this.height){
30282             this.el.setHeight(this.height);
30283         }
30284     },
30285     
30286     // private
30287     renderField : function(f){
30288         f.fieldEl = this.fieldTpl.append(this.el, [
30289                f.id, f.fieldLabel,
30290                f.labelStyle||this.labelStyle||'',
30291                this.elementStyle||'',
30292                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30293                f.itemCls||this.itemCls||'',
30294                f.width ? f.width + this.padWidth : 160 + this.padWidth
30295        ],true);
30296     }
30297 });
30298  
30299
30300 /**
30301  * @class Roo.form.FieldSet
30302  * @extends Roo.form.Layout
30303  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30304  * @constructor
30305  * @param {Object} config Configuration options
30306  */
30307 Roo.form.FieldSet = function(config){
30308     Roo.form.FieldSet.superclass.constructor.call(this, config);
30309 };
30310
30311 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30312     /**
30313      * @cfg {String} legend
30314      * The text to display as the legend for the FieldSet (defaults to '')
30315      */
30316     /**
30317      * @cfg {String/Object} autoCreate
30318      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30319      */
30320
30321     // private
30322     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30323
30324     // private
30325     onRender : function(ct, position){
30326         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30327         if(this.legend){
30328             this.setLegend(this.legend);
30329         }
30330     },
30331
30332     // private
30333     setLegend : function(text){
30334         if(this.rendered){
30335             this.el.child('legend').update(text);
30336         }
30337     }
30338 });/*
30339  * Based on:
30340  * Ext JS Library 1.1.1
30341  * Copyright(c) 2006-2007, Ext JS, LLC.
30342  *
30343  * Originally Released Under LGPL - original licence link has changed is not relivant.
30344  *
30345  * Fork - LGPL
30346  * <script type="text/javascript">
30347  */
30348 /**
30349  * @class Roo.form.VTypes
30350  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30351  * @singleton
30352  */
30353 Roo.form.VTypes = function(){
30354     // closure these in so they are only created once.
30355     var alpha = /^[a-zA-Z_]+$/;
30356     var alphanum = /^[a-zA-Z0-9_]+$/;
30357     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30358     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30359
30360     // All these messages and functions are configurable
30361     return {
30362         /**
30363          * The function used to validate email addresses
30364          * @param {String} value The email address
30365          */
30366         'email' : function(v){
30367             return email.test(v);
30368         },
30369         /**
30370          * The error text to display when the email validation function returns false
30371          * @type String
30372          */
30373         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30374         /**
30375          * The keystroke filter mask to be applied on email input
30376          * @type RegExp
30377          */
30378         'emailMask' : /[a-z0-9_\.\-@]/i,
30379
30380         /**
30381          * The function used to validate URLs
30382          * @param {String} value The URL
30383          */
30384         'url' : function(v){
30385             return url.test(v);
30386         },
30387         /**
30388          * The error text to display when the url validation function returns false
30389          * @type String
30390          */
30391         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30392         
30393         /**
30394          * The function used to validate alpha values
30395          * @param {String} value The value
30396          */
30397         'alpha' : function(v){
30398             return alpha.test(v);
30399         },
30400         /**
30401          * The error text to display when the alpha validation function returns false
30402          * @type String
30403          */
30404         'alphaText' : 'This field should only contain letters and _',
30405         /**
30406          * The keystroke filter mask to be applied on alpha input
30407          * @type RegExp
30408          */
30409         'alphaMask' : /[a-z_]/i,
30410
30411         /**
30412          * The function used to validate alphanumeric values
30413          * @param {String} value The value
30414          */
30415         'alphanum' : function(v){
30416             return alphanum.test(v);
30417         },
30418         /**
30419          * The error text to display when the alphanumeric validation function returns false
30420          * @type String
30421          */
30422         'alphanumText' : 'This field should only contain letters, numbers and _',
30423         /**
30424          * The keystroke filter mask to be applied on alphanumeric input
30425          * @type RegExp
30426          */
30427         'alphanumMask' : /[a-z0-9_]/i
30428     };
30429 }();//<script type="text/javascript">
30430
30431 /**
30432  * @class Roo.form.FCKeditor
30433  * @extends Roo.form.TextArea
30434  * Wrapper around the FCKEditor http://www.fckeditor.net
30435  * @constructor
30436  * Creates a new FCKeditor
30437  * @param {Object} config Configuration options
30438  */
30439 Roo.form.FCKeditor = function(config){
30440     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30441     this.addEvents({
30442          /**
30443          * @event editorinit
30444          * Fired when the editor is initialized - you can add extra handlers here..
30445          * @param {FCKeditor} this
30446          * @param {Object} the FCK object.
30447          */
30448         editorinit : true
30449     });
30450     
30451     
30452 };
30453 Roo.form.FCKeditor.editors = { };
30454 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30455 {
30456     //defaultAutoCreate : {
30457     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30458     //},
30459     // private
30460     /**
30461      * @cfg {Object} fck options - see fck manual for details.
30462      */
30463     fckconfig : false,
30464     
30465     /**
30466      * @cfg {Object} fck toolbar set (Basic or Default)
30467      */
30468     toolbarSet : 'Basic',
30469     /**
30470      * @cfg {Object} fck BasePath
30471      */ 
30472     basePath : '/fckeditor/',
30473     
30474     
30475     frame : false,
30476     
30477     value : '',
30478     
30479    
30480     onRender : function(ct, position)
30481     {
30482         if(!this.el){
30483             this.defaultAutoCreate = {
30484                 tag: "textarea",
30485                 style:"width:300px;height:60px;",
30486                 autocomplete: "new-password"
30487             };
30488         }
30489         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30490         /*
30491         if(this.grow){
30492             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30493             if(this.preventScrollbars){
30494                 this.el.setStyle("overflow", "hidden");
30495             }
30496             this.el.setHeight(this.growMin);
30497         }
30498         */
30499         //console.log('onrender' + this.getId() );
30500         Roo.form.FCKeditor.editors[this.getId()] = this;
30501          
30502
30503         this.replaceTextarea() ;
30504         
30505     },
30506     
30507     getEditor : function() {
30508         return this.fckEditor;
30509     },
30510     /**
30511      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30512      * @param {Mixed} value The value to set
30513      */
30514     
30515     
30516     setValue : function(value)
30517     {
30518         //console.log('setValue: ' + value);
30519         
30520         if(typeof(value) == 'undefined') { // not sure why this is happending...
30521             return;
30522         }
30523         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30524         
30525         //if(!this.el || !this.getEditor()) {
30526         //    this.value = value;
30527             //this.setValue.defer(100,this,[value]);    
30528         //    return;
30529         //} 
30530         
30531         if(!this.getEditor()) {
30532             return;
30533         }
30534         
30535         this.getEditor().SetData(value);
30536         
30537         //
30538
30539     },
30540
30541     /**
30542      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30543      * @return {Mixed} value The field value
30544      */
30545     getValue : function()
30546     {
30547         
30548         if (this.frame && this.frame.dom.style.display == 'none') {
30549             return Roo.form.FCKeditor.superclass.getValue.call(this);
30550         }
30551         
30552         if(!this.el || !this.getEditor()) {
30553            
30554            // this.getValue.defer(100,this); 
30555             return this.value;
30556         }
30557        
30558         
30559         var value=this.getEditor().GetData();
30560         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30561         return Roo.form.FCKeditor.superclass.getValue.call(this);
30562         
30563
30564     },
30565
30566     /**
30567      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30568      * @return {Mixed} value The field value
30569      */
30570     getRawValue : function()
30571     {
30572         if (this.frame && this.frame.dom.style.display == 'none') {
30573             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30574         }
30575         
30576         if(!this.el || !this.getEditor()) {
30577             //this.getRawValue.defer(100,this); 
30578             return this.value;
30579             return;
30580         }
30581         
30582         
30583         
30584         var value=this.getEditor().GetData();
30585         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30586         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30587          
30588     },
30589     
30590     setSize : function(w,h) {
30591         
30592         
30593         
30594         //if (this.frame && this.frame.dom.style.display == 'none') {
30595         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30596         //    return;
30597         //}
30598         //if(!this.el || !this.getEditor()) {
30599         //    this.setSize.defer(100,this, [w,h]); 
30600         //    return;
30601         //}
30602         
30603         
30604         
30605         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30606         
30607         this.frame.dom.setAttribute('width', w);
30608         this.frame.dom.setAttribute('height', h);
30609         this.frame.setSize(w,h);
30610         
30611     },
30612     
30613     toggleSourceEdit : function(value) {
30614         
30615       
30616          
30617         this.el.dom.style.display = value ? '' : 'none';
30618         this.frame.dom.style.display = value ?  'none' : '';
30619         
30620     },
30621     
30622     
30623     focus: function(tag)
30624     {
30625         if (this.frame.dom.style.display == 'none') {
30626             return Roo.form.FCKeditor.superclass.focus.call(this);
30627         }
30628         if(!this.el || !this.getEditor()) {
30629             this.focus.defer(100,this, [tag]); 
30630             return;
30631         }
30632         
30633         
30634         
30635         
30636         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30637         this.getEditor().Focus();
30638         if (tgs.length) {
30639             if (!this.getEditor().Selection.GetSelection()) {
30640                 this.focus.defer(100,this, [tag]); 
30641                 return;
30642             }
30643             
30644             
30645             var r = this.getEditor().EditorDocument.createRange();
30646             r.setStart(tgs[0],0);
30647             r.setEnd(tgs[0],0);
30648             this.getEditor().Selection.GetSelection().removeAllRanges();
30649             this.getEditor().Selection.GetSelection().addRange(r);
30650             this.getEditor().Focus();
30651         }
30652         
30653     },
30654     
30655     
30656     
30657     replaceTextarea : function()
30658     {
30659         if ( document.getElementById( this.getId() + '___Frame' ) ) {
30660             return ;
30661         }
30662         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30663         //{
30664             // We must check the elements firstly using the Id and then the name.
30665         var oTextarea = document.getElementById( this.getId() );
30666         
30667         var colElementsByName = document.getElementsByName( this.getId() ) ;
30668          
30669         oTextarea.style.display = 'none' ;
30670
30671         if ( oTextarea.tabIndex ) {            
30672             this.TabIndex = oTextarea.tabIndex ;
30673         }
30674         
30675         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30676         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30677         this.frame = Roo.get(this.getId() + '___Frame')
30678     },
30679     
30680     _getConfigHtml : function()
30681     {
30682         var sConfig = '' ;
30683
30684         for ( var o in this.fckconfig ) {
30685             sConfig += sConfig.length > 0  ? '&amp;' : '';
30686             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30687         }
30688
30689         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30690     },
30691     
30692     
30693     _getIFrameHtml : function()
30694     {
30695         var sFile = 'fckeditor.html' ;
30696         /* no idea what this is about..
30697         try
30698         {
30699             if ( (/fcksource=true/i).test( window.top.location.search ) )
30700                 sFile = 'fckeditor.original.html' ;
30701         }
30702         catch (e) { 
30703         */
30704
30705         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30706         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30707         
30708         
30709         var html = '<iframe id="' + this.getId() +
30710             '___Frame" src="' + sLink +
30711             '" width="' + this.width +
30712             '" height="' + this.height + '"' +
30713             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30714             ' frameborder="0" scrolling="no"></iframe>' ;
30715
30716         return html ;
30717     },
30718     
30719     _insertHtmlBefore : function( html, element )
30720     {
30721         if ( element.insertAdjacentHTML )       {
30722             // IE
30723             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30724         } else { // Gecko
30725             var oRange = document.createRange() ;
30726             oRange.setStartBefore( element ) ;
30727             var oFragment = oRange.createContextualFragment( html );
30728             element.parentNode.insertBefore( oFragment, element ) ;
30729         }
30730     }
30731     
30732     
30733   
30734     
30735     
30736     
30737     
30738
30739 });
30740
30741 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30742
30743 function FCKeditor_OnComplete(editorInstance){
30744     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30745     f.fckEditor = editorInstance;
30746     //console.log("loaded");
30747     f.fireEvent('editorinit', f, editorInstance);
30748
30749   
30750
30751  
30752
30753
30754
30755
30756
30757
30758
30759
30760
30761
30762
30763
30764
30765
30766
30767 //<script type="text/javascript">
30768 /**
30769  * @class Roo.form.GridField
30770  * @extends Roo.form.Field
30771  * Embed a grid (or editable grid into a form)
30772  * STATUS ALPHA
30773  * 
30774  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30775  * it needs 
30776  * xgrid.store = Roo.data.Store
30777  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30778  * xgrid.store.reader = Roo.data.JsonReader 
30779  * 
30780  * 
30781  * @constructor
30782  * Creates a new GridField
30783  * @param {Object} config Configuration options
30784  */
30785 Roo.form.GridField = function(config){
30786     Roo.form.GridField.superclass.constructor.call(this, config);
30787      
30788 };
30789
30790 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30791     /**
30792      * @cfg {Number} width  - used to restrict width of grid..
30793      */
30794     width : 100,
30795     /**
30796      * @cfg {Number} height - used to restrict height of grid..
30797      */
30798     height : 50,
30799      /**
30800      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30801          * 
30802          *}
30803      */
30804     xgrid : false, 
30805     /**
30806      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30807      * {tag: "input", type: "checkbox", autocomplete: "off"})
30808      */
30809    // defaultAutoCreate : { tag: 'div' },
30810     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30811     /**
30812      * @cfg {String} addTitle Text to include for adding a title.
30813      */
30814     addTitle : false,
30815     //
30816     onResize : function(){
30817         Roo.form.Field.superclass.onResize.apply(this, arguments);
30818     },
30819
30820     initEvents : function(){
30821         // Roo.form.Checkbox.superclass.initEvents.call(this);
30822         // has no events...
30823        
30824     },
30825
30826
30827     getResizeEl : function(){
30828         return this.wrap;
30829     },
30830
30831     getPositionEl : function(){
30832         return this.wrap;
30833     },
30834
30835     // private
30836     onRender : function(ct, position){
30837         
30838         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30839         var style = this.style;
30840         delete this.style;
30841         
30842         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30843         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30844         this.viewEl = this.wrap.createChild({ tag: 'div' });
30845         if (style) {
30846             this.viewEl.applyStyles(style);
30847         }
30848         if (this.width) {
30849             this.viewEl.setWidth(this.width);
30850         }
30851         if (this.height) {
30852             this.viewEl.setHeight(this.height);
30853         }
30854         //if(this.inputValue !== undefined){
30855         //this.setValue(this.value);
30856         
30857         
30858         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30859         
30860         
30861         this.grid.render();
30862         this.grid.getDataSource().on('remove', this.refreshValue, this);
30863         this.grid.getDataSource().on('update', this.refreshValue, this);
30864         this.grid.on('afteredit', this.refreshValue, this);
30865  
30866     },
30867      
30868     
30869     /**
30870      * Sets the value of the item. 
30871      * @param {String} either an object  or a string..
30872      */
30873     setValue : function(v){
30874         //this.value = v;
30875         v = v || []; // empty set..
30876         // this does not seem smart - it really only affects memoryproxy grids..
30877         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30878             var ds = this.grid.getDataSource();
30879             // assumes a json reader..
30880             var data = {}
30881             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30882             ds.loadData( data);
30883         }
30884         // clear selection so it does not get stale.
30885         if (this.grid.sm) { 
30886             this.grid.sm.clearSelections();
30887         }
30888         
30889         Roo.form.GridField.superclass.setValue.call(this, v);
30890         this.refreshValue();
30891         // should load data in the grid really....
30892     },
30893     
30894     // private
30895     refreshValue: function() {
30896          var val = [];
30897         this.grid.getDataSource().each(function(r) {
30898             val.push(r.data);
30899         });
30900         this.el.dom.value = Roo.encode(val);
30901     }
30902     
30903      
30904     
30905     
30906 });/*
30907  * Based on:
30908  * Ext JS Library 1.1.1
30909  * Copyright(c) 2006-2007, Ext JS, LLC.
30910  *
30911  * Originally Released Under LGPL - original licence link has changed is not relivant.
30912  *
30913  * Fork - LGPL
30914  * <script type="text/javascript">
30915  */
30916 /**
30917  * @class Roo.form.DisplayField
30918  * @extends Roo.form.Field
30919  * A generic Field to display non-editable data.
30920  * @cfg {Boolean} closable (true|false) default false
30921  * @constructor
30922  * Creates a new Display Field item.
30923  * @param {Object} config Configuration options
30924  */
30925 Roo.form.DisplayField = function(config){
30926     Roo.form.DisplayField.superclass.constructor.call(this, config);
30927     
30928     this.addEvents({
30929         /**
30930          * @event close
30931          * Fires after the click the close btn
30932              * @param {Roo.form.DisplayField} this
30933              */
30934         close : true
30935     });
30936 };
30937
30938 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30939     inputType:      'hidden',
30940     allowBlank:     true,
30941     readOnly:         true,
30942     
30943  
30944     /**
30945      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30946      */
30947     focusClass : undefined,
30948     /**
30949      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30950      */
30951     fieldClass: 'x-form-field',
30952     
30953      /**
30954      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30955      */
30956     valueRenderer: undefined,
30957     
30958     width: 100,
30959     /**
30960      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30961      * {tag: "input", type: "checkbox", autocomplete: "off"})
30962      */
30963      
30964  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30965  
30966     closable : false,
30967     
30968     onResize : function(){
30969         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30970         
30971     },
30972
30973     initEvents : function(){
30974         // Roo.form.Checkbox.superclass.initEvents.call(this);
30975         // has no events...
30976         
30977         if(this.closable){
30978             this.closeEl.on('click', this.onClose, this);
30979         }
30980        
30981     },
30982
30983
30984     getResizeEl : function(){
30985         return this.wrap;
30986     },
30987
30988     getPositionEl : function(){
30989         return this.wrap;
30990     },
30991
30992     // private
30993     onRender : function(ct, position){
30994         
30995         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30996         //if(this.inputValue !== undefined){
30997         this.wrap = this.el.wrap();
30998         
30999         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31000         
31001         if(this.closable){
31002             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31003         }
31004         
31005         if (this.bodyStyle) {
31006             this.viewEl.applyStyles(this.bodyStyle);
31007         }
31008         //this.viewEl.setStyle('padding', '2px');
31009         
31010         this.setValue(this.value);
31011         
31012     },
31013 /*
31014     // private
31015     initValue : Roo.emptyFn,
31016
31017   */
31018
31019         // private
31020     onClick : function(){
31021         
31022     },
31023
31024     /**
31025      * Sets the checked state of the checkbox.
31026      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31027      */
31028     setValue : function(v){
31029         this.value = v;
31030         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31031         // this might be called before we have a dom element..
31032         if (!this.viewEl) {
31033             return;
31034         }
31035         this.viewEl.dom.innerHTML = html;
31036         Roo.form.DisplayField.superclass.setValue.call(this, v);
31037
31038     },
31039     
31040     onClose : function(e)
31041     {
31042         e.preventDefault();
31043         
31044         this.fireEvent('close', this);
31045     }
31046 });/*
31047  * 
31048  * Licence- LGPL
31049  * 
31050  */
31051
31052 /**
31053  * @class Roo.form.DayPicker
31054  * @extends Roo.form.Field
31055  * A Day picker show [M] [T] [W] ....
31056  * @constructor
31057  * Creates a new Day Picker
31058  * @param {Object} config Configuration options
31059  */
31060 Roo.form.DayPicker= function(config){
31061     Roo.form.DayPicker.superclass.constructor.call(this, config);
31062      
31063 };
31064
31065 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31066     /**
31067      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31068      */
31069     focusClass : undefined,
31070     /**
31071      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31072      */
31073     fieldClass: "x-form-field",
31074    
31075     /**
31076      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31077      * {tag: "input", type: "checkbox", autocomplete: "off"})
31078      */
31079     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31080     
31081    
31082     actionMode : 'viewEl', 
31083     //
31084     // private
31085  
31086     inputType : 'hidden',
31087     
31088      
31089     inputElement: false, // real input element?
31090     basedOn: false, // ????
31091     
31092     isFormField: true, // not sure where this is needed!!!!
31093
31094     onResize : function(){
31095         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31096         if(!this.boxLabel){
31097             this.el.alignTo(this.wrap, 'c-c');
31098         }
31099     },
31100
31101     initEvents : function(){
31102         Roo.form.Checkbox.superclass.initEvents.call(this);
31103         this.el.on("click", this.onClick,  this);
31104         this.el.on("change", this.onClick,  this);
31105     },
31106
31107
31108     getResizeEl : function(){
31109         return this.wrap;
31110     },
31111
31112     getPositionEl : function(){
31113         return this.wrap;
31114     },
31115
31116     
31117     // private
31118     onRender : function(ct, position){
31119         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31120        
31121         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31122         
31123         var r1 = '<table><tr>';
31124         var r2 = '<tr class="x-form-daypick-icons">';
31125         for (var i=0; i < 7; i++) {
31126             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31127             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31128         }
31129         
31130         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31131         viewEl.select('img').on('click', this.onClick, this);
31132         this.viewEl = viewEl;   
31133         
31134         
31135         // this will not work on Chrome!!!
31136         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31137         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31138         
31139         
31140           
31141
31142     },
31143
31144     // private
31145     initValue : Roo.emptyFn,
31146
31147     /**
31148      * Returns the checked state of the checkbox.
31149      * @return {Boolean} True if checked, else false
31150      */
31151     getValue : function(){
31152         return this.el.dom.value;
31153         
31154     },
31155
31156         // private
31157     onClick : function(e){ 
31158         //this.setChecked(!this.checked);
31159         Roo.get(e.target).toggleClass('x-menu-item-checked');
31160         this.refreshValue();
31161         //if(this.el.dom.checked != this.checked){
31162         //    this.setValue(this.el.dom.checked);
31163        // }
31164     },
31165     
31166     // private
31167     refreshValue : function()
31168     {
31169         var val = '';
31170         this.viewEl.select('img',true).each(function(e,i,n)  {
31171             val += e.is(".x-menu-item-checked") ? String(n) : '';
31172         });
31173         this.setValue(val, true);
31174     },
31175
31176     /**
31177      * Sets the checked state of the checkbox.
31178      * On is always based on a string comparison between inputValue and the param.
31179      * @param {Boolean/String} value - the value to set 
31180      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31181      */
31182     setValue : function(v,suppressEvent){
31183         if (!this.el.dom) {
31184             return;
31185         }
31186         var old = this.el.dom.value ;
31187         this.el.dom.value = v;
31188         if (suppressEvent) {
31189             return ;
31190         }
31191          
31192         // update display..
31193         this.viewEl.select('img',true).each(function(e,i,n)  {
31194             
31195             var on = e.is(".x-menu-item-checked");
31196             var newv = v.indexOf(String(n)) > -1;
31197             if (on != newv) {
31198                 e.toggleClass('x-menu-item-checked');
31199             }
31200             
31201         });
31202         
31203         
31204         this.fireEvent('change', this, v, old);
31205         
31206         
31207     },
31208    
31209     // handle setting of hidden value by some other method!!?!?
31210     setFromHidden: function()
31211     {
31212         if(!this.el){
31213             return;
31214         }
31215         //console.log("SET FROM HIDDEN");
31216         //alert('setFrom hidden');
31217         this.setValue(this.el.dom.value);
31218     },
31219     
31220     onDestroy : function()
31221     {
31222         if(this.viewEl){
31223             Roo.get(this.viewEl).remove();
31224         }
31225          
31226         Roo.form.DayPicker.superclass.onDestroy.call(this);
31227     }
31228
31229 });/*
31230  * RooJS Library 1.1.1
31231  * Copyright(c) 2008-2011  Alan Knowles
31232  *
31233  * License - LGPL
31234  */
31235  
31236
31237 /**
31238  * @class Roo.form.ComboCheck
31239  * @extends Roo.form.ComboBox
31240  * A combobox for multiple select items.
31241  *
31242  * FIXME - could do with a reset button..
31243  * 
31244  * @constructor
31245  * Create a new ComboCheck
31246  * @param {Object} config Configuration options
31247  */
31248 Roo.form.ComboCheck = function(config){
31249     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31250     // should verify some data...
31251     // like
31252     // hiddenName = required..
31253     // displayField = required
31254     // valudField == required
31255     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31256     var _t = this;
31257     Roo.each(req, function(e) {
31258         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31259             throw "Roo.form.ComboCheck : missing value for: " + e;
31260         }
31261     });
31262     
31263     
31264 };
31265
31266 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31267      
31268      
31269     editable : false,
31270      
31271     selectedClass: 'x-menu-item-checked', 
31272     
31273     // private
31274     onRender : function(ct, position){
31275         var _t = this;
31276         
31277         
31278         
31279         if(!this.tpl){
31280             var cls = 'x-combo-list';
31281
31282             
31283             this.tpl =  new Roo.Template({
31284                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31285                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31286                    '<span>{' + this.displayField + '}</span>' +
31287                     '</div>' 
31288                 
31289             });
31290         }
31291  
31292         
31293         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31294         this.view.singleSelect = false;
31295         this.view.multiSelect = true;
31296         this.view.toggleSelect = true;
31297         this.pageTb.add(new Roo.Toolbar.Fill(), {
31298             
31299             text: 'Done',
31300             handler: function()
31301             {
31302                 _t.collapse();
31303             }
31304         });
31305     },
31306     
31307     onViewOver : function(e, t){
31308         // do nothing...
31309         return;
31310         
31311     },
31312     
31313     onViewClick : function(doFocus,index){
31314         return;
31315         
31316     },
31317     select: function () {
31318         //Roo.log("SELECT CALLED");
31319     },
31320      
31321     selectByValue : function(xv, scrollIntoView){
31322         var ar = this.getValueArray();
31323         var sels = [];
31324         
31325         Roo.each(ar, function(v) {
31326             if(v === undefined || v === null){
31327                 return;
31328             }
31329             var r = this.findRecord(this.valueField, v);
31330             if(r){
31331                 sels.push(this.store.indexOf(r))
31332                 
31333             }
31334         },this);
31335         this.view.select(sels);
31336         return false;
31337     },
31338     
31339     
31340     
31341     onSelect : function(record, index){
31342        // Roo.log("onselect Called");
31343        // this is only called by the clear button now..
31344         this.view.clearSelections();
31345         this.setValue('[]');
31346         if (this.value != this.valueBefore) {
31347             this.fireEvent('change', this, this.value, this.valueBefore);
31348             this.valueBefore = this.value;
31349         }
31350     },
31351     getValueArray : function()
31352     {
31353         var ar = [] ;
31354         
31355         try {
31356             //Roo.log(this.value);
31357             if (typeof(this.value) == 'undefined') {
31358                 return [];
31359             }
31360             var ar = Roo.decode(this.value);
31361             return  ar instanceof Array ? ar : []; //?? valid?
31362             
31363         } catch(e) {
31364             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31365             return [];
31366         }
31367          
31368     },
31369     expand : function ()
31370     {
31371         
31372         Roo.form.ComboCheck.superclass.expand.call(this);
31373         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31374         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31375         
31376
31377     },
31378     
31379     collapse : function(){
31380         Roo.form.ComboCheck.superclass.collapse.call(this);
31381         var sl = this.view.getSelectedIndexes();
31382         var st = this.store;
31383         var nv = [];
31384         var tv = [];
31385         var r;
31386         Roo.each(sl, function(i) {
31387             r = st.getAt(i);
31388             nv.push(r.get(this.valueField));
31389         },this);
31390         this.setValue(Roo.encode(nv));
31391         if (this.value != this.valueBefore) {
31392
31393             this.fireEvent('change', this, this.value, this.valueBefore);
31394             this.valueBefore = this.value;
31395         }
31396         
31397     },
31398     
31399     setValue : function(v){
31400         // Roo.log(v);
31401         this.value = v;
31402         
31403         var vals = this.getValueArray();
31404         var tv = [];
31405         Roo.each(vals, function(k) {
31406             var r = this.findRecord(this.valueField, k);
31407             if(r){
31408                 tv.push(r.data[this.displayField]);
31409             }else if(this.valueNotFoundText !== undefined){
31410                 tv.push( this.valueNotFoundText );
31411             }
31412         },this);
31413        // Roo.log(tv);
31414         
31415         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31416         this.hiddenField.value = v;
31417         this.value = v;
31418     }
31419     
31420 });/*
31421  * Based on:
31422  * Ext JS Library 1.1.1
31423  * Copyright(c) 2006-2007, Ext JS, LLC.
31424  *
31425  * Originally Released Under LGPL - original licence link has changed is not relivant.
31426  *
31427  * Fork - LGPL
31428  * <script type="text/javascript">
31429  */
31430  
31431 /**
31432  * @class Roo.form.Signature
31433  * @extends Roo.form.Field
31434  * Signature field.  
31435  * @constructor
31436  * 
31437  * @param {Object} config Configuration options
31438  */
31439
31440 Roo.form.Signature = function(config){
31441     Roo.form.Signature.superclass.constructor.call(this, config);
31442     
31443     this.addEvents({// not in used??
31444          /**
31445          * @event confirm
31446          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31447              * @param {Roo.form.Signature} combo This combo box
31448              */
31449         'confirm' : true,
31450         /**
31451          * @event reset
31452          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31453              * @param {Roo.form.ComboBox} combo This combo box
31454              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31455              */
31456         'reset' : true
31457     });
31458 };
31459
31460 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31461     /**
31462      * @cfg {Object} labels Label to use when rendering a form.
31463      * defaults to 
31464      * labels : { 
31465      *      clear : "Clear",
31466      *      confirm : "Confirm"
31467      *  }
31468      */
31469     labels : { 
31470         clear : "Clear",
31471         confirm : "Confirm"
31472     },
31473     /**
31474      * @cfg {Number} width The signature panel width (defaults to 300)
31475      */
31476     width: 300,
31477     /**
31478      * @cfg {Number} height The signature panel height (defaults to 100)
31479      */
31480     height : 100,
31481     /**
31482      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31483      */
31484     allowBlank : false,
31485     
31486     //private
31487     // {Object} signPanel The signature SVG panel element (defaults to {})
31488     signPanel : {},
31489     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31490     isMouseDown : false,
31491     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31492     isConfirmed : false,
31493     // {String} signatureTmp SVG mapping string (defaults to empty string)
31494     signatureTmp : '',
31495     
31496     
31497     defaultAutoCreate : { // modified by initCompnoent..
31498         tag: "input",
31499         type:"hidden"
31500     },
31501
31502     // private
31503     onRender : function(ct, position){
31504         
31505         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31506         
31507         this.wrap = this.el.wrap({
31508             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31509         });
31510         
31511         this.createToolbar(this);
31512         this.signPanel = this.wrap.createChild({
31513                 tag: 'div',
31514                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31515             }, this.el
31516         );
31517             
31518         this.svgID = Roo.id();
31519         this.svgEl = this.signPanel.createChild({
31520               xmlns : 'http://www.w3.org/2000/svg',
31521               tag : 'svg',
31522               id : this.svgID + "-svg",
31523               width: this.width,
31524               height: this.height,
31525               viewBox: '0 0 '+this.width+' '+this.height,
31526               cn : [
31527                 {
31528                     tag: "rect",
31529                     id: this.svgID + "-svg-r",
31530                     width: this.width,
31531                     height: this.height,
31532                     fill: "#ffa"
31533                 },
31534                 {
31535                     tag: "line",
31536                     id: this.svgID + "-svg-l",
31537                     x1: "0", // start
31538                     y1: (this.height*0.8), // start set the line in 80% of height
31539                     x2: this.width, // end
31540                     y2: (this.height*0.8), // end set the line in 80% of height
31541                     'stroke': "#666",
31542                     'stroke-width': "1",
31543                     'stroke-dasharray': "3",
31544                     'shape-rendering': "crispEdges",
31545                     'pointer-events': "none"
31546                 },
31547                 {
31548                     tag: "path",
31549                     id: this.svgID + "-svg-p",
31550                     'stroke': "navy",
31551                     'stroke-width': "3",
31552                     'fill': "none",
31553                     'pointer-events': 'none'
31554                 }
31555               ]
31556         });
31557         this.createSVG();
31558         this.svgBox = this.svgEl.dom.getScreenCTM();
31559     },
31560     createSVG : function(){ 
31561         var svg = this.signPanel;
31562         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31563         var t = this;
31564
31565         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31566         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31567         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31568         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31569         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31570         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31571         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31572         
31573     },
31574     isTouchEvent : function(e){
31575         return e.type.match(/^touch/);
31576     },
31577     getCoords : function (e) {
31578         var pt    = this.svgEl.dom.createSVGPoint();
31579         pt.x = e.clientX; 
31580         pt.y = e.clientY;
31581         if (this.isTouchEvent(e)) {
31582             pt.x =  e.targetTouches[0].clientX;
31583             pt.y = e.targetTouches[0].clientY;
31584         }
31585         var a = this.svgEl.dom.getScreenCTM();
31586         var b = a.inverse();
31587         var mx = pt.matrixTransform(b);
31588         return mx.x + ',' + mx.y;
31589     },
31590     //mouse event headler 
31591     down : function (e) {
31592         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31593         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31594         
31595         this.isMouseDown = true;
31596         
31597         e.preventDefault();
31598     },
31599     move : function (e) {
31600         if (this.isMouseDown) {
31601             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31602             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31603         }
31604         
31605         e.preventDefault();
31606     },
31607     up : function (e) {
31608         this.isMouseDown = false;
31609         var sp = this.signatureTmp.split(' ');
31610         
31611         if(sp.length > 1){
31612             if(!sp[sp.length-2].match(/^L/)){
31613                 sp.pop();
31614                 sp.pop();
31615                 sp.push("");
31616                 this.signatureTmp = sp.join(" ");
31617             }
31618         }
31619         if(this.getValue() != this.signatureTmp){
31620             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31621             this.isConfirmed = false;
31622         }
31623         e.preventDefault();
31624     },
31625     
31626     /**
31627      * Protected method that will not generally be called directly. It
31628      * is called when the editor creates its toolbar. Override this method if you need to
31629      * add custom toolbar buttons.
31630      * @param {HtmlEditor} editor
31631      */
31632     createToolbar : function(editor){
31633          function btn(id, toggle, handler){
31634             var xid = fid + '-'+ id ;
31635             return {
31636                 id : xid,
31637                 cmd : id,
31638                 cls : 'x-btn-icon x-edit-'+id,
31639                 enableToggle:toggle !== false,
31640                 scope: editor, // was editor...
31641                 handler:handler||editor.relayBtnCmd,
31642                 clickEvent:'mousedown',
31643                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31644                 tabIndex:-1
31645             };
31646         }
31647         
31648         
31649         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31650         this.tb = tb;
31651         this.tb.add(
31652            {
31653                 cls : ' x-signature-btn x-signature-'+id,
31654                 scope: editor, // was editor...
31655                 handler: this.reset,
31656                 clickEvent:'mousedown',
31657                 text: this.labels.clear
31658             },
31659             {
31660                  xtype : 'Fill',
31661                  xns: Roo.Toolbar
31662             }, 
31663             {
31664                 cls : '  x-signature-btn x-signature-'+id,
31665                 scope: editor, // was editor...
31666                 handler: this.confirmHandler,
31667                 clickEvent:'mousedown',
31668                 text: this.labels.confirm
31669             }
31670         );
31671     
31672     },
31673     //public
31674     /**
31675      * when user is clicked confirm then show this image.....
31676      * 
31677      * @return {String} Image Data URI
31678      */
31679     getImageDataURI : function(){
31680         var svg = this.svgEl.dom.parentNode.innerHTML;
31681         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31682         return src; 
31683     },
31684     /**
31685      * 
31686      * @return {Boolean} this.isConfirmed
31687      */
31688     getConfirmed : function(){
31689         return this.isConfirmed;
31690     },
31691     /**
31692      * 
31693      * @return {Number} this.width
31694      */
31695     getWidth : function(){
31696         return this.width;
31697     },
31698     /**
31699      * 
31700      * @return {Number} this.height
31701      */
31702     getHeight : function(){
31703         return this.height;
31704     },
31705     // private
31706     getSignature : function(){
31707         return this.signatureTmp;
31708     },
31709     // private
31710     reset : function(){
31711         this.signatureTmp = '';
31712         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31713         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31714         this.isConfirmed = false;
31715         Roo.form.Signature.superclass.reset.call(this);
31716     },
31717     setSignature : function(s){
31718         this.signatureTmp = s;
31719         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31720         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31721         this.setValue(s);
31722         this.isConfirmed = false;
31723         Roo.form.Signature.superclass.reset.call(this);
31724     }, 
31725     test : function(){
31726 //        Roo.log(this.signPanel.dom.contentWindow.up())
31727     },
31728     //private
31729     setConfirmed : function(){
31730         
31731         
31732         
31733 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31734     },
31735     // private
31736     confirmHandler : function(){
31737         if(!this.getSignature()){
31738             return;
31739         }
31740         
31741         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31742         this.setValue(this.getSignature());
31743         this.isConfirmed = true;
31744         
31745         this.fireEvent('confirm', this);
31746     },
31747     // private
31748     // Subclasses should provide the validation implementation by overriding this
31749     validateValue : function(value){
31750         if(this.allowBlank){
31751             return true;
31752         }
31753         
31754         if(this.isConfirmed){
31755             return true;
31756         }
31757         return false;
31758     }
31759 });/*
31760  * Based on:
31761  * Ext JS Library 1.1.1
31762  * Copyright(c) 2006-2007, Ext JS, LLC.
31763  *
31764  * Originally Released Under LGPL - original licence link has changed is not relivant.
31765  *
31766  * Fork - LGPL
31767  * <script type="text/javascript">
31768  */
31769  
31770
31771 /**
31772  * @class Roo.form.ComboBox
31773  * @extends Roo.form.TriggerField
31774  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31775  * @constructor
31776  * Create a new ComboBox.
31777  * @param {Object} config Configuration options
31778  */
31779 Roo.form.Select = function(config){
31780     Roo.form.Select.superclass.constructor.call(this, config);
31781      
31782 };
31783
31784 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31785     /**
31786      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31787      */
31788     /**
31789      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31790      * rendering into an Roo.Editor, defaults to false)
31791      */
31792     /**
31793      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31794      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31795      */
31796     /**
31797      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31798      */
31799     /**
31800      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31801      * the dropdown list (defaults to undefined, with no header element)
31802      */
31803
31804      /**
31805      * @cfg {String/Roo.Template} tpl The template to use to render the output
31806      */
31807      
31808     // private
31809     defaultAutoCreate : {tag: "select"  },
31810     /**
31811      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31812      */
31813     listWidth: undefined,
31814     /**
31815      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31816      * mode = 'remote' or 'text' if mode = 'local')
31817      */
31818     displayField: undefined,
31819     /**
31820      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31821      * mode = 'remote' or 'value' if mode = 'local'). 
31822      * Note: use of a valueField requires the user make a selection
31823      * in order for a value to be mapped.
31824      */
31825     valueField: undefined,
31826     
31827     
31828     /**
31829      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31830      * field's data value (defaults to the underlying DOM element's name)
31831      */
31832     hiddenName: undefined,
31833     /**
31834      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31835      */
31836     listClass: '',
31837     /**
31838      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31839      */
31840     selectedClass: 'x-combo-selected',
31841     /**
31842      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31843      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31844      * which displays a downward arrow icon).
31845      */
31846     triggerClass : 'x-form-arrow-trigger',
31847     /**
31848      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31849      */
31850     shadow:'sides',
31851     /**
31852      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31853      * anchor positions (defaults to 'tl-bl')
31854      */
31855     listAlign: 'tl-bl?',
31856     /**
31857      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31858      */
31859     maxHeight: 300,
31860     /**
31861      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31862      * query specified by the allQuery config option (defaults to 'query')
31863      */
31864     triggerAction: 'query',
31865     /**
31866      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31867      * (defaults to 4, does not apply if editable = false)
31868      */
31869     minChars : 4,
31870     /**
31871      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31872      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31873      */
31874     typeAhead: false,
31875     /**
31876      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31877      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31878      */
31879     queryDelay: 500,
31880     /**
31881      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31882      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31883      */
31884     pageSize: 0,
31885     /**
31886      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31887      * when editable = true (defaults to false)
31888      */
31889     selectOnFocus:false,
31890     /**
31891      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31892      */
31893     queryParam: 'query',
31894     /**
31895      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31896      * when mode = 'remote' (defaults to 'Loading...')
31897      */
31898     loadingText: 'Loading...',
31899     /**
31900      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31901      */
31902     resizable: false,
31903     /**
31904      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31905      */
31906     handleHeight : 8,
31907     /**
31908      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31909      * traditional select (defaults to true)
31910      */
31911     editable: true,
31912     /**
31913      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31914      */
31915     allQuery: '',
31916     /**
31917      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31918      */
31919     mode: 'remote',
31920     /**
31921      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31922      * listWidth has a higher value)
31923      */
31924     minListWidth : 70,
31925     /**
31926      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31927      * allow the user to set arbitrary text into the field (defaults to false)
31928      */
31929     forceSelection:false,
31930     /**
31931      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31932      * if typeAhead = true (defaults to 250)
31933      */
31934     typeAheadDelay : 250,
31935     /**
31936      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31937      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31938      */
31939     valueNotFoundText : undefined,
31940     
31941     /**
31942      * @cfg {String} defaultValue The value displayed after loading the store.
31943      */
31944     defaultValue: '',
31945     
31946     /**
31947      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31948      */
31949     blockFocus : false,
31950     
31951     /**
31952      * @cfg {Boolean} disableClear Disable showing of clear button.
31953      */
31954     disableClear : false,
31955     /**
31956      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31957      */
31958     alwaysQuery : false,
31959     
31960     //private
31961     addicon : false,
31962     editicon: false,
31963     
31964     // element that contains real text value.. (when hidden is used..)
31965      
31966     // private
31967     onRender : function(ct, position){
31968         Roo.form.Field.prototype.onRender.call(this, ct, position);
31969         
31970         if(this.store){
31971             this.store.on('beforeload', this.onBeforeLoad, this);
31972             this.store.on('load', this.onLoad, this);
31973             this.store.on('loadexception', this.onLoadException, this);
31974             this.store.load({});
31975         }
31976         
31977         
31978         
31979     },
31980
31981     // private
31982     initEvents : function(){
31983         //Roo.form.ComboBox.superclass.initEvents.call(this);
31984  
31985     },
31986
31987     onDestroy : function(){
31988        
31989         if(this.store){
31990             this.store.un('beforeload', this.onBeforeLoad, this);
31991             this.store.un('load', this.onLoad, this);
31992             this.store.un('loadexception', this.onLoadException, this);
31993         }
31994         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31995     },
31996
31997     // private
31998     fireKey : function(e){
31999         if(e.isNavKeyPress() && !this.list.isVisible()){
32000             this.fireEvent("specialkey", this, e);
32001         }
32002     },
32003
32004     // private
32005     onResize: function(w, h){
32006         
32007         return; 
32008     
32009         
32010     },
32011
32012     /**
32013      * Allow or prevent the user from directly editing the field text.  If false is passed,
32014      * the user will only be able to select from the items defined in the dropdown list.  This method
32015      * is the runtime equivalent of setting the 'editable' config option at config time.
32016      * @param {Boolean} value True to allow the user to directly edit the field text
32017      */
32018     setEditable : function(value){
32019          
32020     },
32021
32022     // private
32023     onBeforeLoad : function(){
32024         
32025         Roo.log("Select before load");
32026         return;
32027     
32028         this.innerList.update(this.loadingText ?
32029                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32030         //this.restrictHeight();
32031         this.selectedIndex = -1;
32032     },
32033
32034     // private
32035     onLoad : function(){
32036
32037     
32038         var dom = this.el.dom;
32039         dom.innerHTML = '';
32040          var od = dom.ownerDocument;
32041          
32042         if (this.emptyText) {
32043             var op = od.createElement('option');
32044             op.setAttribute('value', '');
32045             op.innerHTML = String.format('{0}', this.emptyText);
32046             dom.appendChild(op);
32047         }
32048         if(this.store.getCount() > 0){
32049            
32050             var vf = this.valueField;
32051             var df = this.displayField;
32052             this.store.data.each(function(r) {
32053                 // which colmsn to use... testing - cdoe / title..
32054                 var op = od.createElement('option');
32055                 op.setAttribute('value', r.data[vf]);
32056                 op.innerHTML = String.format('{0}', r.data[df]);
32057                 dom.appendChild(op);
32058             });
32059             if (typeof(this.defaultValue != 'undefined')) {
32060                 this.setValue(this.defaultValue);
32061             }
32062             
32063              
32064         }else{
32065             //this.onEmptyResults();
32066         }
32067         //this.el.focus();
32068     },
32069     // private
32070     onLoadException : function()
32071     {
32072         dom.innerHTML = '';
32073             
32074         Roo.log("Select on load exception");
32075         return;
32076     
32077         this.collapse();
32078         Roo.log(this.store.reader.jsonData);
32079         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32080             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32081         }
32082         
32083         
32084     },
32085     // private
32086     onTypeAhead : function(){
32087          
32088     },
32089
32090     // private
32091     onSelect : function(record, index){
32092         Roo.log('on select?');
32093         return;
32094         if(this.fireEvent('beforeselect', this, record, index) !== false){
32095             this.setFromData(index > -1 ? record.data : false);
32096             this.collapse();
32097             this.fireEvent('select', this, record, index);
32098         }
32099     },
32100
32101     /**
32102      * Returns the currently selected field value or empty string if no value is set.
32103      * @return {String} value The selected value
32104      */
32105     getValue : function(){
32106         var dom = this.el.dom;
32107         this.value = dom.options[dom.selectedIndex].value;
32108         return this.value;
32109         
32110     },
32111
32112     /**
32113      * Clears any text/value currently set in the field
32114      */
32115     clearValue : function(){
32116         this.value = '';
32117         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32118         
32119     },
32120
32121     /**
32122      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32123      * will be displayed in the field.  If the value does not match the data value of an existing item,
32124      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32125      * Otherwise the field will be blank (although the value will still be set).
32126      * @param {String} value The value to match
32127      */
32128     setValue : function(v){
32129         var d = this.el.dom;
32130         for (var i =0; i < d.options.length;i++) {
32131             if (v == d.options[i].value) {
32132                 d.selectedIndex = i;
32133                 this.value = v;
32134                 return;
32135             }
32136         }
32137         this.clearValue();
32138     },
32139     /**
32140      * @property {Object} the last set data for the element
32141      */
32142     
32143     lastData : false,
32144     /**
32145      * Sets the value of the field based on a object which is related to the record format for the store.
32146      * @param {Object} value the value to set as. or false on reset?
32147      */
32148     setFromData : function(o){
32149         Roo.log('setfrom data?');
32150          
32151         
32152         
32153     },
32154     // private
32155     reset : function(){
32156         this.clearValue();
32157     },
32158     // private
32159     findRecord : function(prop, value){
32160         
32161         return false;
32162     
32163         var record;
32164         if(this.store.getCount() > 0){
32165             this.store.each(function(r){
32166                 if(r.data[prop] == value){
32167                     record = r;
32168                     return false;
32169                 }
32170                 return true;
32171             });
32172         }
32173         return record;
32174     },
32175     
32176     getName: function()
32177     {
32178         // returns hidden if it's set..
32179         if (!this.rendered) {return ''};
32180         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32181         
32182     },
32183      
32184
32185     
32186
32187     // private
32188     onEmptyResults : function(){
32189         Roo.log('empty results');
32190         //this.collapse();
32191     },
32192
32193     /**
32194      * Returns true if the dropdown list is expanded, else false.
32195      */
32196     isExpanded : function(){
32197         return false;
32198     },
32199
32200     /**
32201      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32202      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32203      * @param {String} value The data value of the item to select
32204      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32205      * selected item if it is not currently in view (defaults to true)
32206      * @return {Boolean} True if the value matched an item in the list, else false
32207      */
32208     selectByValue : function(v, scrollIntoView){
32209         Roo.log('select By Value');
32210         return false;
32211     
32212         if(v !== undefined && v !== null){
32213             var r = this.findRecord(this.valueField || this.displayField, v);
32214             if(r){
32215                 this.select(this.store.indexOf(r), scrollIntoView);
32216                 return true;
32217             }
32218         }
32219         return false;
32220     },
32221
32222     /**
32223      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32224      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32225      * @param {Number} index The zero-based index of the list item to select
32226      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32227      * selected item if it is not currently in view (defaults to true)
32228      */
32229     select : function(index, scrollIntoView){
32230         Roo.log('select ');
32231         return  ;
32232         
32233         this.selectedIndex = index;
32234         this.view.select(index);
32235         if(scrollIntoView !== false){
32236             var el = this.view.getNode(index);
32237             if(el){
32238                 this.innerList.scrollChildIntoView(el, false);
32239             }
32240         }
32241     },
32242
32243       
32244
32245     // private
32246     validateBlur : function(){
32247         
32248         return;
32249         
32250     },
32251
32252     // private
32253     initQuery : function(){
32254         this.doQuery(this.getRawValue());
32255     },
32256
32257     // private
32258     doForce : function(){
32259         if(this.el.dom.value.length > 0){
32260             this.el.dom.value =
32261                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32262              
32263         }
32264     },
32265
32266     /**
32267      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32268      * query allowing the query action to be canceled if needed.
32269      * @param {String} query The SQL query to execute
32270      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32271      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32272      * saved in the current store (defaults to false)
32273      */
32274     doQuery : function(q, forceAll){
32275         
32276         Roo.log('doQuery?');
32277         if(q === undefined || q === null){
32278             q = '';
32279         }
32280         var qe = {
32281             query: q,
32282             forceAll: forceAll,
32283             combo: this,
32284             cancel:false
32285         };
32286         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32287             return false;
32288         }
32289         q = qe.query;
32290         forceAll = qe.forceAll;
32291         if(forceAll === true || (q.length >= this.minChars)){
32292             if(this.lastQuery != q || this.alwaysQuery){
32293                 this.lastQuery = q;
32294                 if(this.mode == 'local'){
32295                     this.selectedIndex = -1;
32296                     if(forceAll){
32297                         this.store.clearFilter();
32298                     }else{
32299                         this.store.filter(this.displayField, q);
32300                     }
32301                     this.onLoad();
32302                 }else{
32303                     this.store.baseParams[this.queryParam] = q;
32304                     this.store.load({
32305                         params: this.getParams(q)
32306                     });
32307                     this.expand();
32308                 }
32309             }else{
32310                 this.selectedIndex = -1;
32311                 this.onLoad();   
32312             }
32313         }
32314     },
32315
32316     // private
32317     getParams : function(q){
32318         var p = {};
32319         //p[this.queryParam] = q;
32320         if(this.pageSize){
32321             p.start = 0;
32322             p.limit = this.pageSize;
32323         }
32324         return p;
32325     },
32326
32327     /**
32328      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32329      */
32330     collapse : function(){
32331         
32332     },
32333
32334     // private
32335     collapseIf : function(e){
32336         
32337     },
32338
32339     /**
32340      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32341      */
32342     expand : function(){
32343         
32344     } ,
32345
32346     // private
32347      
32348
32349     /** 
32350     * @cfg {Boolean} grow 
32351     * @hide 
32352     */
32353     /** 
32354     * @cfg {Number} growMin 
32355     * @hide 
32356     */
32357     /** 
32358     * @cfg {Number} growMax 
32359     * @hide 
32360     */
32361     /**
32362      * @hide
32363      * @method autoSize
32364      */
32365     
32366     setWidth : function()
32367     {
32368         
32369     },
32370     getResizeEl : function(){
32371         return this.el;
32372     }
32373 });//<script type="text/javasscript">
32374  
32375
32376 /**
32377  * @class Roo.DDView
32378  * A DnD enabled version of Roo.View.
32379  * @param {Element/String} container The Element in which to create the View.
32380  * @param {String} tpl The template string used to create the markup for each element of the View
32381  * @param {Object} config The configuration properties. These include all the config options of
32382  * {@link Roo.View} plus some specific to this class.<br>
32383  * <p>
32384  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32385  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32386  * <p>
32387  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32388 .x-view-drag-insert-above {
32389         border-top:1px dotted #3366cc;
32390 }
32391 .x-view-drag-insert-below {
32392         border-bottom:1px dotted #3366cc;
32393 }
32394 </code></pre>
32395  * 
32396  */
32397  
32398 Roo.DDView = function(container, tpl, config) {
32399     Roo.DDView.superclass.constructor.apply(this, arguments);
32400     this.getEl().setStyle("outline", "0px none");
32401     this.getEl().unselectable();
32402     if (this.dragGroup) {
32403                 this.setDraggable(this.dragGroup.split(","));
32404     }
32405     if (this.dropGroup) {
32406                 this.setDroppable(this.dropGroup.split(","));
32407     }
32408     if (this.deletable) {
32409         this.setDeletable();
32410     }
32411     this.isDirtyFlag = false;
32412         this.addEvents({
32413                 "drop" : true
32414         });
32415 };
32416
32417 Roo.extend(Roo.DDView, Roo.View, {
32418 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32419 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32420 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32421 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32422
32423         isFormField: true,
32424
32425         reset: Roo.emptyFn,
32426         
32427         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32428
32429         validate: function() {
32430                 return true;
32431         },
32432         
32433         destroy: function() {
32434                 this.purgeListeners();
32435                 this.getEl.removeAllListeners();
32436                 this.getEl().remove();
32437                 if (this.dragZone) {
32438                         if (this.dragZone.destroy) {
32439                                 this.dragZone.destroy();
32440                         }
32441                 }
32442                 if (this.dropZone) {
32443                         if (this.dropZone.destroy) {
32444                                 this.dropZone.destroy();
32445                         }
32446                 }
32447         },
32448
32449 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32450         getName: function() {
32451                 return this.name;
32452         },
32453
32454 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32455         setValue: function(v) {
32456                 if (!this.store) {
32457                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32458                 }
32459                 var data = {};
32460                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32461                 this.store.proxy = new Roo.data.MemoryProxy(data);
32462                 this.store.load();
32463         },
32464
32465 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32466         getValue: function() {
32467                 var result = '(';
32468                 this.store.each(function(rec) {
32469                         result += rec.id + ',';
32470                 });
32471                 return result.substr(0, result.length - 1) + ')';
32472         },
32473         
32474         getIds: function() {
32475                 var i = 0, result = new Array(this.store.getCount());
32476                 this.store.each(function(rec) {
32477                         result[i++] = rec.id;
32478                 });
32479                 return result;
32480         },
32481         
32482         isDirty: function() {
32483                 return this.isDirtyFlag;
32484         },
32485
32486 /**
32487  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32488  *      whole Element becomes the target, and this causes the drop gesture to append.
32489  */
32490     getTargetFromEvent : function(e) {
32491                 var target = e.getTarget();
32492                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32493                 target = target.parentNode;
32494                 }
32495                 if (!target) {
32496                         target = this.el.dom.lastChild || this.el.dom;
32497                 }
32498                 return target;
32499     },
32500
32501 /**
32502  *      Create the drag data which consists of an object which has the property "ddel" as
32503  *      the drag proxy element. 
32504  */
32505     getDragData : function(e) {
32506         var target = this.findItemFromChild(e.getTarget());
32507                 if(target) {
32508                         this.handleSelection(e);
32509                         var selNodes = this.getSelectedNodes();
32510             var dragData = {
32511                 source: this,
32512                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32513                 nodes: selNodes,
32514                 records: []
32515                         };
32516                         var selectedIndices = this.getSelectedIndexes();
32517                         for (var i = 0; i < selectedIndices.length; i++) {
32518                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32519                         }
32520                         if (selNodes.length == 1) {
32521                                 dragData.ddel = target.cloneNode(true); // the div element
32522                         } else {
32523                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32524                                 div.className = 'multi-proxy';
32525                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32526                                         div.appendChild(selNodes[i].cloneNode(true));
32527                                 }
32528                                 dragData.ddel = div;
32529                         }
32530             //console.log(dragData)
32531             //console.log(dragData.ddel.innerHTML)
32532                         return dragData;
32533                 }
32534         //console.log('nodragData')
32535                 return false;
32536     },
32537     
32538 /**     Specify to which ddGroup items in this DDView may be dragged. */
32539     setDraggable: function(ddGroup) {
32540         if (ddGroup instanceof Array) {
32541                 Roo.each(ddGroup, this.setDraggable, this);
32542                 return;
32543         }
32544         if (this.dragZone) {
32545                 this.dragZone.addToGroup(ddGroup);
32546         } else {
32547                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32548                                 containerScroll: true,
32549                                 ddGroup: ddGroup 
32550
32551                         });
32552 //                      Draggability implies selection. DragZone's mousedown selects the element.
32553                         if (!this.multiSelect) { this.singleSelect = true; }
32554
32555 //                      Wire the DragZone's handlers up to methods in *this*
32556                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32557                 }
32558     },
32559
32560 /**     Specify from which ddGroup this DDView accepts drops. */
32561     setDroppable: function(ddGroup) {
32562         if (ddGroup instanceof Array) {
32563                 Roo.each(ddGroup, this.setDroppable, this);
32564                 return;
32565         }
32566         if (this.dropZone) {
32567                 this.dropZone.addToGroup(ddGroup);
32568         } else {
32569                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32570                                 containerScroll: true,
32571                                 ddGroup: ddGroup
32572                         });
32573
32574 //                      Wire the DropZone's handlers up to methods in *this*
32575                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32576                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32577                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32578                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32579                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32580                 }
32581     },
32582
32583 /**     Decide whether to drop above or below a View node. */
32584     getDropPoint : function(e, n, dd){
32585         if (n == this.el.dom) { return "above"; }
32586                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32587                 var c = t + (b - t) / 2;
32588                 var y = Roo.lib.Event.getPageY(e);
32589                 if(y <= c) {
32590                         return "above";
32591                 }else{
32592                         return "below";
32593                 }
32594     },
32595
32596     onNodeEnter : function(n, dd, e, data){
32597                 return false;
32598     },
32599     
32600     onNodeOver : function(n, dd, e, data){
32601                 var pt = this.getDropPoint(e, n, dd);
32602                 // set the insert point style on the target node
32603                 var dragElClass = this.dropNotAllowed;
32604                 if (pt) {
32605                         var targetElClass;
32606                         if (pt == "above"){
32607                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32608                                 targetElClass = "x-view-drag-insert-above";
32609                         } else {
32610                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32611                                 targetElClass = "x-view-drag-insert-below";
32612                         }
32613                         if (this.lastInsertClass != targetElClass){
32614                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32615                                 this.lastInsertClass = targetElClass;
32616                         }
32617                 }
32618                 return dragElClass;
32619         },
32620
32621     onNodeOut : function(n, dd, e, data){
32622                 this.removeDropIndicators(n);
32623     },
32624
32625     onNodeDrop : function(n, dd, e, data){
32626         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32627                 return false;
32628         }
32629         var pt = this.getDropPoint(e, n, dd);
32630                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32631                 if (pt == "below") { insertAt++; }
32632                 for (var i = 0; i < data.records.length; i++) {
32633                         var r = data.records[i];
32634                         var dup = this.store.getById(r.id);
32635                         if (dup && (dd != this.dragZone)) {
32636                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32637                         } else {
32638                                 if (data.copy) {
32639                                         this.store.insert(insertAt++, r.copy());
32640                                 } else {
32641                                         data.source.isDirtyFlag = true;
32642                                         r.store.remove(r);
32643                                         this.store.insert(insertAt++, r);
32644                                 }
32645                                 this.isDirtyFlag = true;
32646                         }
32647                 }
32648                 this.dragZone.cachedTarget = null;
32649                 return true;
32650     },
32651
32652     removeDropIndicators : function(n){
32653                 if(n){
32654                         Roo.fly(n).removeClass([
32655                                 "x-view-drag-insert-above",
32656                                 "x-view-drag-insert-below"]);
32657                         this.lastInsertClass = "_noclass";
32658                 }
32659     },
32660
32661 /**
32662  *      Utility method. Add a delete option to the DDView's context menu.
32663  *      @param {String} imageUrl The URL of the "delete" icon image.
32664  */
32665         setDeletable: function(imageUrl) {
32666                 if (!this.singleSelect && !this.multiSelect) {
32667                         this.singleSelect = true;
32668                 }
32669                 var c = this.getContextMenu();
32670                 this.contextMenu.on("itemclick", function(item) {
32671                         switch (item.id) {
32672                                 case "delete":
32673                                         this.remove(this.getSelectedIndexes());
32674                                         break;
32675                         }
32676                 }, this);
32677                 this.contextMenu.add({
32678                         icon: imageUrl,
32679                         id: "delete",
32680                         text: 'Delete'
32681                 });
32682         },
32683         
32684 /**     Return the context menu for this DDView. */
32685         getContextMenu: function() {
32686                 if (!this.contextMenu) {
32687 //                      Create the View's context menu
32688                         this.contextMenu = new Roo.menu.Menu({
32689                                 id: this.id + "-contextmenu"
32690                         });
32691                         this.el.on("contextmenu", this.showContextMenu, this);
32692                 }
32693                 return this.contextMenu;
32694         },
32695         
32696         disableContextMenu: function() {
32697                 if (this.contextMenu) {
32698                         this.el.un("contextmenu", this.showContextMenu, this);
32699                 }
32700         },
32701
32702         showContextMenu: function(e, item) {
32703         item = this.findItemFromChild(e.getTarget());
32704                 if (item) {
32705                         e.stopEvent();
32706                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32707                         this.contextMenu.showAt(e.getXY());
32708             }
32709     },
32710
32711 /**
32712  *      Remove {@link Roo.data.Record}s at the specified indices.
32713  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32714  */
32715     remove: function(selectedIndices) {
32716                 selectedIndices = [].concat(selectedIndices);
32717                 for (var i = 0; i < selectedIndices.length; i++) {
32718                         var rec = this.store.getAt(selectedIndices[i]);
32719                         this.store.remove(rec);
32720                 }
32721     },
32722
32723 /**
32724  *      Double click fires the event, but also, if this is draggable, and there is only one other
32725  *      related DropZone, it transfers the selected node.
32726  */
32727     onDblClick : function(e){
32728         var item = this.findItemFromChild(e.getTarget());
32729         if(item){
32730             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32731                 return false;
32732             }
32733             if (this.dragGroup) {
32734                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32735                     while (targets.indexOf(this.dropZone) > -1) {
32736                             targets.remove(this.dropZone);
32737                                 }
32738                     if (targets.length == 1) {
32739                                         this.dragZone.cachedTarget = null;
32740                         var el = Roo.get(targets[0].getEl());
32741                         var box = el.getBox(true);
32742                         targets[0].onNodeDrop(el.dom, {
32743                                 target: el.dom,
32744                                 xy: [box.x, box.y + box.height - 1]
32745                         }, null, this.getDragData(e));
32746                     }
32747                 }
32748         }
32749     },
32750     
32751     handleSelection: function(e) {
32752                 this.dragZone.cachedTarget = null;
32753         var item = this.findItemFromChild(e.getTarget());
32754         if (!item) {
32755                 this.clearSelections(true);
32756                 return;
32757         }
32758                 if (item && (this.multiSelect || this.singleSelect)){
32759                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32760                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32761                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32762                                 this.unselect(item);
32763                         } else {
32764                                 this.select(item, this.multiSelect && e.ctrlKey);
32765                                 this.lastSelection = item;
32766                         }
32767                 }
32768     },
32769
32770     onItemClick : function(item, index, e){
32771                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32772                         return false;
32773                 }
32774                 return true;
32775     },
32776
32777     unselect : function(nodeInfo, suppressEvent){
32778                 var node = this.getNode(nodeInfo);
32779                 if(node && this.isSelected(node)){
32780                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32781                                 Roo.fly(node).removeClass(this.selectedClass);
32782                                 this.selections.remove(node);
32783                                 if(!suppressEvent){
32784                                         this.fireEvent("selectionchange", this, this.selections);
32785                                 }
32786                         }
32787                 }
32788     }
32789 });
32790 /*
32791  * Based on:
32792  * Ext JS Library 1.1.1
32793  * Copyright(c) 2006-2007, Ext JS, LLC.
32794  *
32795  * Originally Released Under LGPL - original licence link has changed is not relivant.
32796  *
32797  * Fork - LGPL
32798  * <script type="text/javascript">
32799  */
32800  
32801 /**
32802  * @class Roo.LayoutManager
32803  * @extends Roo.util.Observable
32804  * Base class for layout managers.
32805  */
32806 Roo.LayoutManager = function(container, config){
32807     Roo.LayoutManager.superclass.constructor.call(this);
32808     this.el = Roo.get(container);
32809     // ie scrollbar fix
32810     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32811         document.body.scroll = "no";
32812     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32813         this.el.position('relative');
32814     }
32815     this.id = this.el.id;
32816     this.el.addClass("x-layout-container");
32817     /** false to disable window resize monitoring @type Boolean */
32818     this.monitorWindowResize = true;
32819     this.regions = {};
32820     this.addEvents({
32821         /**
32822          * @event layout
32823          * Fires when a layout is performed. 
32824          * @param {Roo.LayoutManager} this
32825          */
32826         "layout" : true,
32827         /**
32828          * @event regionresized
32829          * Fires when the user resizes a region. 
32830          * @param {Roo.LayoutRegion} region The resized region
32831          * @param {Number} newSize The new size (width for east/west, height for north/south)
32832          */
32833         "regionresized" : true,
32834         /**
32835          * @event regioncollapsed
32836          * Fires when a region is collapsed. 
32837          * @param {Roo.LayoutRegion} region The collapsed region
32838          */
32839         "regioncollapsed" : true,
32840         /**
32841          * @event regionexpanded
32842          * Fires when a region is expanded.  
32843          * @param {Roo.LayoutRegion} region The expanded region
32844          */
32845         "regionexpanded" : true
32846     });
32847     this.updating = false;
32848     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32849 };
32850
32851 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32852     /**
32853      * Returns true if this layout is currently being updated
32854      * @return {Boolean}
32855      */
32856     isUpdating : function(){
32857         return this.updating; 
32858     },
32859     
32860     /**
32861      * Suspend the LayoutManager from doing auto-layouts while
32862      * making multiple add or remove calls
32863      */
32864     beginUpdate : function(){
32865         this.updating = true;    
32866     },
32867     
32868     /**
32869      * Restore auto-layouts and optionally disable the manager from performing a layout
32870      * @param {Boolean} noLayout true to disable a layout update 
32871      */
32872     endUpdate : function(noLayout){
32873         this.updating = false;
32874         if(!noLayout){
32875             this.layout();
32876         }    
32877     },
32878     
32879     layout: function(){
32880         
32881     },
32882     
32883     onRegionResized : function(region, newSize){
32884         this.fireEvent("regionresized", region, newSize);
32885         this.layout();
32886     },
32887     
32888     onRegionCollapsed : function(region){
32889         this.fireEvent("regioncollapsed", region);
32890     },
32891     
32892     onRegionExpanded : function(region){
32893         this.fireEvent("regionexpanded", region);
32894     },
32895         
32896     /**
32897      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32898      * performs box-model adjustments.
32899      * @return {Object} The size as an object {width: (the width), height: (the height)}
32900      */
32901     getViewSize : function(){
32902         var size;
32903         if(this.el.dom != document.body){
32904             size = this.el.getSize();
32905         }else{
32906             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32907         }
32908         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32909         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32910         return size;
32911     },
32912     
32913     /**
32914      * Returns the Element this layout is bound to.
32915      * @return {Roo.Element}
32916      */
32917     getEl : function(){
32918         return this.el;
32919     },
32920     
32921     /**
32922      * Returns the specified region.
32923      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32924      * @return {Roo.LayoutRegion}
32925      */
32926     getRegion : function(target){
32927         return this.regions[target.toLowerCase()];
32928     },
32929     
32930     onWindowResize : function(){
32931         if(this.monitorWindowResize){
32932             this.layout();
32933         }
32934     }
32935 });/*
32936  * Based on:
32937  * Ext JS Library 1.1.1
32938  * Copyright(c) 2006-2007, Ext JS, LLC.
32939  *
32940  * Originally Released Under LGPL - original licence link has changed is not relivant.
32941  *
32942  * Fork - LGPL
32943  * <script type="text/javascript">
32944  */
32945 /**
32946  * @class Roo.BorderLayout
32947  * @extends Roo.LayoutManager
32948  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32949  * please see: <br><br>
32950  * <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>
32951  * <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>
32952  * Example:
32953  <pre><code>
32954  var layout = new Roo.BorderLayout(document.body, {
32955     north: {
32956         initialSize: 25,
32957         titlebar: false
32958     },
32959     west: {
32960         split:true,
32961         initialSize: 200,
32962         minSize: 175,
32963         maxSize: 400,
32964         titlebar: true,
32965         collapsible: true
32966     },
32967     east: {
32968         split:true,
32969         initialSize: 202,
32970         minSize: 175,
32971         maxSize: 400,
32972         titlebar: true,
32973         collapsible: true
32974     },
32975     south: {
32976         split:true,
32977         initialSize: 100,
32978         minSize: 100,
32979         maxSize: 200,
32980         titlebar: true,
32981         collapsible: true
32982     },
32983     center: {
32984         titlebar: true,
32985         autoScroll:true,
32986         resizeTabs: true,
32987         minTabWidth: 50,
32988         preferredTabWidth: 150
32989     }
32990 });
32991
32992 // shorthand
32993 var CP = Roo.ContentPanel;
32994
32995 layout.beginUpdate();
32996 layout.add("north", new CP("north", "North"));
32997 layout.add("south", new CP("south", {title: "South", closable: true}));
32998 layout.add("west", new CP("west", {title: "West"}));
32999 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33000 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33001 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33002 layout.getRegion("center").showPanel("center1");
33003 layout.endUpdate();
33004 </code></pre>
33005
33006 <b>The container the layout is rendered into can be either the body element or any other element.
33007 If it is not the body element, the container needs to either be an absolute positioned element,
33008 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33009 the container size if it is not the body element.</b>
33010
33011 * @constructor
33012 * Create a new BorderLayout
33013 * @param {String/HTMLElement/Element} container The container this layout is bound to
33014 * @param {Object} config Configuration options
33015  */
33016 Roo.BorderLayout = function(container, config){
33017     config = config || {};
33018     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33019     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33020     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33021         var target = this.factory.validRegions[i];
33022         if(config[target]){
33023             this.addRegion(target, config[target]);
33024         }
33025     }
33026 };
33027
33028 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33029     /**
33030      * Creates and adds a new region if it doesn't already exist.
33031      * @param {String} target The target region key (north, south, east, west or center).
33032      * @param {Object} config The regions config object
33033      * @return {BorderLayoutRegion} The new region
33034      */
33035     addRegion : function(target, config){
33036         if(!this.regions[target]){
33037             var r = this.factory.create(target, this, config);
33038             this.bindRegion(target, r);
33039         }
33040         return this.regions[target];
33041     },
33042
33043     // private (kinda)
33044     bindRegion : function(name, r){
33045         this.regions[name] = r;
33046         r.on("visibilitychange", this.layout, this);
33047         r.on("paneladded", this.layout, this);
33048         r.on("panelremoved", this.layout, this);
33049         r.on("invalidated", this.layout, this);
33050         r.on("resized", this.onRegionResized, this);
33051         r.on("collapsed", this.onRegionCollapsed, this);
33052         r.on("expanded", this.onRegionExpanded, this);
33053     },
33054
33055     /**
33056      * Performs a layout update.
33057      */
33058     layout : function(){
33059         if(this.updating) {
33060             return;
33061         }
33062         var size = this.getViewSize();
33063         var w = size.width;
33064         var h = size.height;
33065         var centerW = w;
33066         var centerH = h;
33067         var centerY = 0;
33068         var centerX = 0;
33069         //var x = 0, y = 0;
33070
33071         var rs = this.regions;
33072         var north = rs["north"];
33073         var south = rs["south"]; 
33074         var west = rs["west"];
33075         var east = rs["east"];
33076         var center = rs["center"];
33077         //if(this.hideOnLayout){ // not supported anymore
33078             //c.el.setStyle("display", "none");
33079         //}
33080         if(north && north.isVisible()){
33081             var b = north.getBox();
33082             var m = north.getMargins();
33083             b.width = w - (m.left+m.right);
33084             b.x = m.left;
33085             b.y = m.top;
33086             centerY = b.height + b.y + m.bottom;
33087             centerH -= centerY;
33088             north.updateBox(this.safeBox(b));
33089         }
33090         if(south && south.isVisible()){
33091             var b = south.getBox();
33092             var m = south.getMargins();
33093             b.width = w - (m.left+m.right);
33094             b.x = m.left;
33095             var totalHeight = (b.height + m.top + m.bottom);
33096             b.y = h - totalHeight + m.top;
33097             centerH -= totalHeight;
33098             south.updateBox(this.safeBox(b));
33099         }
33100         if(west && west.isVisible()){
33101             var b = west.getBox();
33102             var m = west.getMargins();
33103             b.height = centerH - (m.top+m.bottom);
33104             b.x = m.left;
33105             b.y = centerY + m.top;
33106             var totalWidth = (b.width + m.left + m.right);
33107             centerX += totalWidth;
33108             centerW -= totalWidth;
33109             west.updateBox(this.safeBox(b));
33110         }
33111         if(east && east.isVisible()){
33112             var b = east.getBox();
33113             var m = east.getMargins();
33114             b.height = centerH - (m.top+m.bottom);
33115             var totalWidth = (b.width + m.left + m.right);
33116             b.x = w - totalWidth + m.left;
33117             b.y = centerY + m.top;
33118             centerW -= totalWidth;
33119             east.updateBox(this.safeBox(b));
33120         }
33121         if(center){
33122             var m = center.getMargins();
33123             var centerBox = {
33124                 x: centerX + m.left,
33125                 y: centerY + m.top,
33126                 width: centerW - (m.left+m.right),
33127                 height: centerH - (m.top+m.bottom)
33128             };
33129             //if(this.hideOnLayout){
33130                 //center.el.setStyle("display", "block");
33131             //}
33132             center.updateBox(this.safeBox(centerBox));
33133         }
33134         this.el.repaint();
33135         this.fireEvent("layout", this);
33136     },
33137
33138     // private
33139     safeBox : function(box){
33140         box.width = Math.max(0, box.width);
33141         box.height = Math.max(0, box.height);
33142         return box;
33143     },
33144
33145     /**
33146      * Adds a ContentPanel (or subclass) to this layout.
33147      * @param {String} target The target region key (north, south, east, west or center).
33148      * @param {Roo.ContentPanel} panel The panel to add
33149      * @return {Roo.ContentPanel} The added panel
33150      */
33151     add : function(target, panel){
33152          
33153         target = target.toLowerCase();
33154         return this.regions[target].add(panel);
33155     },
33156
33157     /**
33158      * Remove a ContentPanel (or subclass) to this layout.
33159      * @param {String} target The target region key (north, south, east, west or center).
33160      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33161      * @return {Roo.ContentPanel} The removed panel
33162      */
33163     remove : function(target, panel){
33164         target = target.toLowerCase();
33165         return this.regions[target].remove(panel);
33166     },
33167
33168     /**
33169      * Searches all regions for a panel with the specified id
33170      * @param {String} panelId
33171      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33172      */
33173     findPanel : function(panelId){
33174         var rs = this.regions;
33175         for(var target in rs){
33176             if(typeof rs[target] != "function"){
33177                 var p = rs[target].getPanel(panelId);
33178                 if(p){
33179                     return p;
33180                 }
33181             }
33182         }
33183         return null;
33184     },
33185
33186     /**
33187      * Searches all regions for a panel with the specified id and activates (shows) it.
33188      * @param {String/ContentPanel} panelId The panels id or the panel itself
33189      * @return {Roo.ContentPanel} The shown panel or null
33190      */
33191     showPanel : function(panelId) {
33192       var rs = this.regions;
33193       for(var target in rs){
33194          var r = rs[target];
33195          if(typeof r != "function"){
33196             if(r.hasPanel(panelId)){
33197                return r.showPanel(panelId);
33198             }
33199          }
33200       }
33201       return null;
33202    },
33203
33204    /**
33205      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33206      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33207      */
33208     restoreState : function(provider){
33209         if(!provider){
33210             provider = Roo.state.Manager;
33211         }
33212         var sm = new Roo.LayoutStateManager();
33213         sm.init(this, provider);
33214     },
33215
33216     /**
33217      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33218      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33219      * a valid ContentPanel config object.  Example:
33220      * <pre><code>
33221 // Create the main layout
33222 var layout = new Roo.BorderLayout('main-ct', {
33223     west: {
33224         split:true,
33225         minSize: 175,
33226         titlebar: true
33227     },
33228     center: {
33229         title:'Components'
33230     }
33231 }, 'main-ct');
33232
33233 // Create and add multiple ContentPanels at once via configs
33234 layout.batchAdd({
33235    west: {
33236        id: 'source-files',
33237        autoCreate:true,
33238        title:'Ext Source Files',
33239        autoScroll:true,
33240        fitToFrame:true
33241    },
33242    center : {
33243        el: cview,
33244        autoScroll:true,
33245        fitToFrame:true,
33246        toolbar: tb,
33247        resizeEl:'cbody'
33248    }
33249 });
33250 </code></pre>
33251      * @param {Object} regions An object containing ContentPanel configs by region name
33252      */
33253     batchAdd : function(regions){
33254         this.beginUpdate();
33255         for(var rname in regions){
33256             var lr = this.regions[rname];
33257             if(lr){
33258                 this.addTypedPanels(lr, regions[rname]);
33259             }
33260         }
33261         this.endUpdate();
33262     },
33263
33264     // private
33265     addTypedPanels : function(lr, ps){
33266         if(typeof ps == 'string'){
33267             lr.add(new Roo.ContentPanel(ps));
33268         }
33269         else if(ps instanceof Array){
33270             for(var i =0, len = ps.length; i < len; i++){
33271                 this.addTypedPanels(lr, ps[i]);
33272             }
33273         }
33274         else if(!ps.events){ // raw config?
33275             var el = ps.el;
33276             delete ps.el; // prevent conflict
33277             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33278         }
33279         else {  // panel object assumed!
33280             lr.add(ps);
33281         }
33282     },
33283     /**
33284      * Adds a xtype elements to the layout.
33285      * <pre><code>
33286
33287 layout.addxtype({
33288        xtype : 'ContentPanel',
33289        region: 'west',
33290        items: [ .... ]
33291    }
33292 );
33293
33294 layout.addxtype({
33295         xtype : 'NestedLayoutPanel',
33296         region: 'west',
33297         layout: {
33298            center: { },
33299            west: { }   
33300         },
33301         items : [ ... list of content panels or nested layout panels.. ]
33302    }
33303 );
33304 </code></pre>
33305      * @param {Object} cfg Xtype definition of item to add.
33306      */
33307     addxtype : function(cfg)
33308     {
33309         // basically accepts a pannel...
33310         // can accept a layout region..!?!?
33311         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33312         
33313         if (!cfg.xtype.match(/Panel$/)) {
33314             return false;
33315         }
33316         var ret = false;
33317         
33318         if (typeof(cfg.region) == 'undefined') {
33319             Roo.log("Failed to add Panel, region was not set");
33320             Roo.log(cfg);
33321             return false;
33322         }
33323         var region = cfg.region;
33324         delete cfg.region;
33325         
33326           
33327         var xitems = [];
33328         if (cfg.items) {
33329             xitems = cfg.items;
33330             delete cfg.items;
33331         }
33332         var nb = false;
33333         
33334         switch(cfg.xtype) 
33335         {
33336             case 'ContentPanel':  // ContentPanel (el, cfg)
33337             case 'ScrollPanel':  // ContentPanel (el, cfg)
33338             case 'ViewPanel': 
33339                 if(cfg.autoCreate) {
33340                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33341                 } else {
33342                     var el = this.el.createChild();
33343                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33344                 }
33345                 
33346                 this.add(region, ret);
33347                 break;
33348             
33349             
33350             case 'TreePanel': // our new panel!
33351                 cfg.el = this.el.createChild();
33352                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33353                 this.add(region, ret);
33354                 break;
33355             
33356             case 'NestedLayoutPanel': 
33357                 // create a new Layout (which is  a Border Layout...
33358                 var el = this.el.createChild();
33359                 var clayout = cfg.layout;
33360                 delete cfg.layout;
33361                 clayout.items   = clayout.items  || [];
33362                 // replace this exitems with the clayout ones..
33363                 xitems = clayout.items;
33364                  
33365                 
33366                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33367                     cfg.background = false;
33368                 }
33369                 var layout = new Roo.BorderLayout(el, clayout);
33370                 
33371                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33372                 //console.log('adding nested layout panel '  + cfg.toSource());
33373                 this.add(region, ret);
33374                 nb = {}; /// find first...
33375                 break;
33376                 
33377             case 'GridPanel': 
33378             
33379                 // needs grid and region
33380                 
33381                 //var el = this.getRegion(region).el.createChild();
33382                 var el = this.el.createChild();
33383                 // create the grid first...
33384                 
33385                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33386                 delete cfg.grid;
33387                 if (region == 'center' && this.active ) {
33388                     cfg.background = false;
33389                 }
33390                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33391                 
33392                 this.add(region, ret);
33393                 if (cfg.background) {
33394                     ret.on('activate', function(gp) {
33395                         if (!gp.grid.rendered) {
33396                             gp.grid.render();
33397                         }
33398                     });
33399                 } else {
33400                     grid.render();
33401                 }
33402                 break;
33403            
33404            
33405            
33406                 
33407                 
33408                 
33409             default:
33410                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33411                     
33412                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33413                     this.add(region, ret);
33414                 } else {
33415                 
33416                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33417                     return null;
33418                 }
33419                 
33420              // GridPanel (grid, cfg)
33421             
33422         }
33423         this.beginUpdate();
33424         // add children..
33425         var region = '';
33426         var abn = {};
33427         Roo.each(xitems, function(i)  {
33428             region = nb && i.region ? i.region : false;
33429             
33430             var add = ret.addxtype(i);
33431            
33432             if (region) {
33433                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33434                 if (!i.background) {
33435                     abn[region] = nb[region] ;
33436                 }
33437             }
33438             
33439         });
33440         this.endUpdate();
33441
33442         // make the last non-background panel active..
33443         //if (nb) { Roo.log(abn); }
33444         if (nb) {
33445             
33446             for(var r in abn) {
33447                 region = this.getRegion(r);
33448                 if (region) {
33449                     // tried using nb[r], but it does not work..
33450                      
33451                     region.showPanel(abn[r]);
33452                    
33453                 }
33454             }
33455         }
33456         return ret;
33457         
33458     }
33459 });
33460
33461 /**
33462  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33463  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33464  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33465  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33466  * <pre><code>
33467 // shorthand
33468 var CP = Roo.ContentPanel;
33469
33470 var layout = Roo.BorderLayout.create({
33471     north: {
33472         initialSize: 25,
33473         titlebar: false,
33474         panels: [new CP("north", "North")]
33475     },
33476     west: {
33477         split:true,
33478         initialSize: 200,
33479         minSize: 175,
33480         maxSize: 400,
33481         titlebar: true,
33482         collapsible: true,
33483         panels: [new CP("west", {title: "West"})]
33484     },
33485     east: {
33486         split:true,
33487         initialSize: 202,
33488         minSize: 175,
33489         maxSize: 400,
33490         titlebar: true,
33491         collapsible: true,
33492         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33493     },
33494     south: {
33495         split:true,
33496         initialSize: 100,
33497         minSize: 100,
33498         maxSize: 200,
33499         titlebar: true,
33500         collapsible: true,
33501         panels: [new CP("south", {title: "South", closable: true})]
33502     },
33503     center: {
33504         titlebar: true,
33505         autoScroll:true,
33506         resizeTabs: true,
33507         minTabWidth: 50,
33508         preferredTabWidth: 150,
33509         panels: [
33510             new CP("center1", {title: "Close Me", closable: true}),
33511             new CP("center2", {title: "Center Panel", closable: false})
33512         ]
33513     }
33514 }, document.body);
33515
33516 layout.getRegion("center").showPanel("center1");
33517 </code></pre>
33518  * @param config
33519  * @param targetEl
33520  */
33521 Roo.BorderLayout.create = function(config, targetEl){
33522     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33523     layout.beginUpdate();
33524     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33525     for(var j = 0, jlen = regions.length; j < jlen; j++){
33526         var lr = regions[j];
33527         if(layout.regions[lr] && config[lr].panels){
33528             var r = layout.regions[lr];
33529             var ps = config[lr].panels;
33530             layout.addTypedPanels(r, ps);
33531         }
33532     }
33533     layout.endUpdate();
33534     return layout;
33535 };
33536
33537 // private
33538 Roo.BorderLayout.RegionFactory = {
33539     // private
33540     validRegions : ["north","south","east","west","center"],
33541
33542     // private
33543     create : function(target, mgr, config){
33544         target = target.toLowerCase();
33545         if(config.lightweight || config.basic){
33546             return new Roo.BasicLayoutRegion(mgr, config, target);
33547         }
33548         switch(target){
33549             case "north":
33550                 return new Roo.NorthLayoutRegion(mgr, config);
33551             case "south":
33552                 return new Roo.SouthLayoutRegion(mgr, config);
33553             case "east":
33554                 return new Roo.EastLayoutRegion(mgr, config);
33555             case "west":
33556                 return new Roo.WestLayoutRegion(mgr, config);
33557             case "center":
33558                 return new Roo.CenterLayoutRegion(mgr, config);
33559         }
33560         throw 'Layout region "'+target+'" not supported.';
33561     }
33562 };/*
33563  * Based on:
33564  * Ext JS Library 1.1.1
33565  * Copyright(c) 2006-2007, Ext JS, LLC.
33566  *
33567  * Originally Released Under LGPL - original licence link has changed is not relivant.
33568  *
33569  * Fork - LGPL
33570  * <script type="text/javascript">
33571  */
33572  
33573 /**
33574  * @class Roo.BasicLayoutRegion
33575  * @extends Roo.util.Observable
33576  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33577  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33578  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33579  */
33580 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33581     this.mgr = mgr;
33582     this.position  = pos;
33583     this.events = {
33584         /**
33585          * @scope Roo.BasicLayoutRegion
33586          */
33587         
33588         /**
33589          * @event beforeremove
33590          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33591          * @param {Roo.LayoutRegion} this
33592          * @param {Roo.ContentPanel} panel The panel
33593          * @param {Object} e The cancel event object
33594          */
33595         "beforeremove" : true,
33596         /**
33597          * @event invalidated
33598          * Fires when the layout for this region is changed.
33599          * @param {Roo.LayoutRegion} this
33600          */
33601         "invalidated" : true,
33602         /**
33603          * @event visibilitychange
33604          * Fires when this region is shown or hidden 
33605          * @param {Roo.LayoutRegion} this
33606          * @param {Boolean} visibility true or false
33607          */
33608         "visibilitychange" : true,
33609         /**
33610          * @event paneladded
33611          * Fires when a panel is added. 
33612          * @param {Roo.LayoutRegion} this
33613          * @param {Roo.ContentPanel} panel The panel
33614          */
33615         "paneladded" : true,
33616         /**
33617          * @event panelremoved
33618          * Fires when a panel is removed. 
33619          * @param {Roo.LayoutRegion} this
33620          * @param {Roo.ContentPanel} panel The panel
33621          */
33622         "panelremoved" : true,
33623         /**
33624          * @event beforecollapse
33625          * Fires when this region before collapse.
33626          * @param {Roo.LayoutRegion} this
33627          */
33628         "beforecollapse" : true,
33629         /**
33630          * @event collapsed
33631          * Fires when this region is collapsed.
33632          * @param {Roo.LayoutRegion} this
33633          */
33634         "collapsed" : true,
33635         /**
33636          * @event expanded
33637          * Fires when this region is expanded.
33638          * @param {Roo.LayoutRegion} this
33639          */
33640         "expanded" : true,
33641         /**
33642          * @event slideshow
33643          * Fires when this region is slid into view.
33644          * @param {Roo.LayoutRegion} this
33645          */
33646         "slideshow" : true,
33647         /**
33648          * @event slidehide
33649          * Fires when this region slides out of view. 
33650          * @param {Roo.LayoutRegion} this
33651          */
33652         "slidehide" : true,
33653         /**
33654          * @event panelactivated
33655          * Fires when a panel is activated. 
33656          * @param {Roo.LayoutRegion} this
33657          * @param {Roo.ContentPanel} panel The activated panel
33658          */
33659         "panelactivated" : true,
33660         /**
33661          * @event resized
33662          * Fires when the user resizes this region. 
33663          * @param {Roo.LayoutRegion} this
33664          * @param {Number} newSize The new size (width for east/west, height for north/south)
33665          */
33666         "resized" : true
33667     };
33668     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33669     this.panels = new Roo.util.MixedCollection();
33670     this.panels.getKey = this.getPanelId.createDelegate(this);
33671     this.box = null;
33672     this.activePanel = null;
33673     // ensure listeners are added...
33674     
33675     if (config.listeners || config.events) {
33676         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33677             listeners : config.listeners || {},
33678             events : config.events || {}
33679         });
33680     }
33681     
33682     if(skipConfig !== true){
33683         this.applyConfig(config);
33684     }
33685 };
33686
33687 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33688     getPanelId : function(p){
33689         return p.getId();
33690     },
33691     
33692     applyConfig : function(config){
33693         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33694         this.config = config;
33695         
33696     },
33697     
33698     /**
33699      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33700      * the width, for horizontal (north, south) the height.
33701      * @param {Number} newSize The new width or height
33702      */
33703     resizeTo : function(newSize){
33704         var el = this.el ? this.el :
33705                  (this.activePanel ? this.activePanel.getEl() : null);
33706         if(el){
33707             switch(this.position){
33708                 case "east":
33709                 case "west":
33710                     el.setWidth(newSize);
33711                     this.fireEvent("resized", this, newSize);
33712                 break;
33713                 case "north":
33714                 case "south":
33715                     el.setHeight(newSize);
33716                     this.fireEvent("resized", this, newSize);
33717                 break;                
33718             }
33719         }
33720     },
33721     
33722     getBox : function(){
33723         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33724     },
33725     
33726     getMargins : function(){
33727         return this.margins;
33728     },
33729     
33730     updateBox : function(box){
33731         this.box = box;
33732         var el = this.activePanel.getEl();
33733         el.dom.style.left = box.x + "px";
33734         el.dom.style.top = box.y + "px";
33735         this.activePanel.setSize(box.width, box.height);
33736     },
33737     
33738     /**
33739      * Returns the container element for this region.
33740      * @return {Roo.Element}
33741      */
33742     getEl : function(){
33743         return this.activePanel;
33744     },
33745     
33746     /**
33747      * Returns true if this region is currently visible.
33748      * @return {Boolean}
33749      */
33750     isVisible : function(){
33751         return this.activePanel ? true : false;
33752     },
33753     
33754     setActivePanel : function(panel){
33755         panel = this.getPanel(panel);
33756         if(this.activePanel && this.activePanel != panel){
33757             this.activePanel.setActiveState(false);
33758             this.activePanel.getEl().setLeftTop(-10000,-10000);
33759         }
33760         this.activePanel = panel;
33761         panel.setActiveState(true);
33762         if(this.box){
33763             panel.setSize(this.box.width, this.box.height);
33764         }
33765         this.fireEvent("panelactivated", this, panel);
33766         this.fireEvent("invalidated");
33767     },
33768     
33769     /**
33770      * Show the specified panel.
33771      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33772      * @return {Roo.ContentPanel} The shown panel or null
33773      */
33774     showPanel : function(panel){
33775         if(panel = this.getPanel(panel)){
33776             this.setActivePanel(panel);
33777         }
33778         return panel;
33779     },
33780     
33781     /**
33782      * Get the active panel for this region.
33783      * @return {Roo.ContentPanel} The active panel or null
33784      */
33785     getActivePanel : function(){
33786         return this.activePanel;
33787     },
33788     
33789     /**
33790      * Add the passed ContentPanel(s)
33791      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33792      * @return {Roo.ContentPanel} The panel added (if only one was added)
33793      */
33794     add : function(panel){
33795         if(arguments.length > 1){
33796             for(var i = 0, len = arguments.length; i < len; i++) {
33797                 this.add(arguments[i]);
33798             }
33799             return null;
33800         }
33801         if(this.hasPanel(panel)){
33802             this.showPanel(panel);
33803             return panel;
33804         }
33805         var el = panel.getEl();
33806         if(el.dom.parentNode != this.mgr.el.dom){
33807             this.mgr.el.dom.appendChild(el.dom);
33808         }
33809         if(panel.setRegion){
33810             panel.setRegion(this);
33811         }
33812         this.panels.add(panel);
33813         el.setStyle("position", "absolute");
33814         if(!panel.background){
33815             this.setActivePanel(panel);
33816             if(this.config.initialSize && this.panels.getCount()==1){
33817                 this.resizeTo(this.config.initialSize);
33818             }
33819         }
33820         this.fireEvent("paneladded", this, panel);
33821         return panel;
33822     },
33823     
33824     /**
33825      * Returns true if the panel is in this region.
33826      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33827      * @return {Boolean}
33828      */
33829     hasPanel : function(panel){
33830         if(typeof panel == "object"){ // must be panel obj
33831             panel = panel.getId();
33832         }
33833         return this.getPanel(panel) ? true : false;
33834     },
33835     
33836     /**
33837      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33838      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33839      * @param {Boolean} preservePanel Overrides the config preservePanel option
33840      * @return {Roo.ContentPanel} The panel that was removed
33841      */
33842     remove : function(panel, preservePanel){
33843         panel = this.getPanel(panel);
33844         if(!panel){
33845             return null;
33846         }
33847         var e = {};
33848         this.fireEvent("beforeremove", this, panel, e);
33849         if(e.cancel === true){
33850             return null;
33851         }
33852         var panelId = panel.getId();
33853         this.panels.removeKey(panelId);
33854         return panel;
33855     },
33856     
33857     /**
33858      * Returns the panel specified or null if it's not in this region.
33859      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33860      * @return {Roo.ContentPanel}
33861      */
33862     getPanel : function(id){
33863         if(typeof id == "object"){ // must be panel obj
33864             return id;
33865         }
33866         return this.panels.get(id);
33867     },
33868     
33869     /**
33870      * Returns this regions position (north/south/east/west/center).
33871      * @return {String} 
33872      */
33873     getPosition: function(){
33874         return this.position;    
33875     }
33876 });/*
33877  * Based on:
33878  * Ext JS Library 1.1.1
33879  * Copyright(c) 2006-2007, Ext JS, LLC.
33880  *
33881  * Originally Released Under LGPL - original licence link has changed is not relivant.
33882  *
33883  * Fork - LGPL
33884  * <script type="text/javascript">
33885  */
33886  
33887 /**
33888  * @class Roo.LayoutRegion
33889  * @extends Roo.BasicLayoutRegion
33890  * This class represents a region in a layout manager.
33891  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33892  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33893  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33894  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33895  * @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})
33896  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33897  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33898  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33899  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33900  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33901  * @cfg {String}    title           The title for the region (overrides panel titles)
33902  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33903  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33904  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33905  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33906  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33907  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33908  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33909  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33910  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33911  * @cfg {Boolean}   showPin         True to show a pin button
33912  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33913  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33914  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33915  * @cfg {Number}    width           For East/West panels
33916  * @cfg {Number}    height          For North/South panels
33917  * @cfg {Boolean}   split           To show the splitter
33918  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33919  */
33920 Roo.LayoutRegion = function(mgr, config, pos){
33921     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33922     var dh = Roo.DomHelper;
33923     /** This region's container element 
33924     * @type Roo.Element */
33925     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33926     /** This region's title element 
33927     * @type Roo.Element */
33928
33929     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33930         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33931         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33932     ]}, true);
33933     this.titleEl.enableDisplayMode();
33934     /** This region's title text element 
33935     * @type HTMLElement */
33936     this.titleTextEl = this.titleEl.dom.firstChild;
33937     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33938     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33939     this.closeBtn.enableDisplayMode();
33940     this.closeBtn.on("click", this.closeClicked, this);
33941     this.closeBtn.hide();
33942
33943     this.createBody(config);
33944     this.visible = true;
33945     this.collapsed = false;
33946
33947     if(config.hideWhenEmpty){
33948         this.hide();
33949         this.on("paneladded", this.validateVisibility, this);
33950         this.on("panelremoved", this.validateVisibility, this);
33951     }
33952     this.applyConfig(config);
33953 };
33954
33955 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33956
33957     createBody : function(){
33958         /** This region's body element 
33959         * @type Roo.Element */
33960         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33961     },
33962
33963     applyConfig : function(c){
33964         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33965             var dh = Roo.DomHelper;
33966             if(c.titlebar !== false){
33967                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33968                 this.collapseBtn.on("click", this.collapse, this);
33969                 this.collapseBtn.enableDisplayMode();
33970
33971                 if(c.showPin === true || this.showPin){
33972                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33973                     this.stickBtn.enableDisplayMode();
33974                     this.stickBtn.on("click", this.expand, this);
33975                     this.stickBtn.hide();
33976                 }
33977             }
33978             /** This region's collapsed element
33979             * @type Roo.Element */
33980             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33981                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33982             ]}, true);
33983             if(c.floatable !== false){
33984                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33985                this.collapsedEl.on("click", this.collapseClick, this);
33986             }
33987
33988             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33989                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33990                    id: "message", unselectable: "on", style:{"float":"left"}});
33991                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33992              }
33993             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33994             this.expandBtn.on("click", this.expand, this);
33995         }
33996         if(this.collapseBtn){
33997             this.collapseBtn.setVisible(c.collapsible == true);
33998         }
33999         this.cmargins = c.cmargins || this.cmargins ||
34000                          (this.position == "west" || this.position == "east" ?
34001                              {top: 0, left: 2, right:2, bottom: 0} :
34002                              {top: 2, left: 0, right:0, bottom: 2});
34003         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34004         this.bottomTabs = c.tabPosition != "top";
34005         this.autoScroll = c.autoScroll || false;
34006         if(this.autoScroll){
34007             this.bodyEl.setStyle("overflow", "auto");
34008         }else{
34009             this.bodyEl.setStyle("overflow", "hidden");
34010         }
34011         //if(c.titlebar !== false){
34012             if((!c.titlebar && !c.title) || c.titlebar === false){
34013                 this.titleEl.hide();
34014             }else{
34015                 this.titleEl.show();
34016                 if(c.title){
34017                     this.titleTextEl.innerHTML = c.title;
34018                 }
34019             }
34020         //}
34021         this.duration = c.duration || .30;
34022         this.slideDuration = c.slideDuration || .45;
34023         this.config = c;
34024         if(c.collapsed){
34025             this.collapse(true);
34026         }
34027         if(c.hidden){
34028             this.hide();
34029         }
34030     },
34031     /**
34032      * Returns true if this region is currently visible.
34033      * @return {Boolean}
34034      */
34035     isVisible : function(){
34036         return this.visible;
34037     },
34038
34039     /**
34040      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34041      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34042      */
34043     setCollapsedTitle : function(title){
34044         title = title || "&#160;";
34045         if(this.collapsedTitleTextEl){
34046             this.collapsedTitleTextEl.innerHTML = title;
34047         }
34048     },
34049
34050     getBox : function(){
34051         var b;
34052         if(!this.collapsed){
34053             b = this.el.getBox(false, true);
34054         }else{
34055             b = this.collapsedEl.getBox(false, true);
34056         }
34057         return b;
34058     },
34059
34060     getMargins : function(){
34061         return this.collapsed ? this.cmargins : this.margins;
34062     },
34063
34064     highlight : function(){
34065         this.el.addClass("x-layout-panel-dragover");
34066     },
34067
34068     unhighlight : function(){
34069         this.el.removeClass("x-layout-panel-dragover");
34070     },
34071
34072     updateBox : function(box){
34073         this.box = box;
34074         if(!this.collapsed){
34075             this.el.dom.style.left = box.x + "px";
34076             this.el.dom.style.top = box.y + "px";
34077             this.updateBody(box.width, box.height);
34078         }else{
34079             this.collapsedEl.dom.style.left = box.x + "px";
34080             this.collapsedEl.dom.style.top = box.y + "px";
34081             this.collapsedEl.setSize(box.width, box.height);
34082         }
34083         if(this.tabs){
34084             this.tabs.autoSizeTabs();
34085         }
34086     },
34087
34088     updateBody : function(w, h){
34089         if(w !== null){
34090             this.el.setWidth(w);
34091             w -= this.el.getBorderWidth("rl");
34092             if(this.config.adjustments){
34093                 w += this.config.adjustments[0];
34094             }
34095         }
34096         if(h !== null){
34097             this.el.setHeight(h);
34098             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34099             h -= this.el.getBorderWidth("tb");
34100             if(this.config.adjustments){
34101                 h += this.config.adjustments[1];
34102             }
34103             this.bodyEl.setHeight(h);
34104             if(this.tabs){
34105                 h = this.tabs.syncHeight(h);
34106             }
34107         }
34108         if(this.panelSize){
34109             w = w !== null ? w : this.panelSize.width;
34110             h = h !== null ? h : this.panelSize.height;
34111         }
34112         if(this.activePanel){
34113             var el = this.activePanel.getEl();
34114             w = w !== null ? w : el.getWidth();
34115             h = h !== null ? h : el.getHeight();
34116             this.panelSize = {width: w, height: h};
34117             this.activePanel.setSize(w, h);
34118         }
34119         if(Roo.isIE && this.tabs){
34120             this.tabs.el.repaint();
34121         }
34122     },
34123
34124     /**
34125      * Returns the container element for this region.
34126      * @return {Roo.Element}
34127      */
34128     getEl : function(){
34129         return this.el;
34130     },
34131
34132     /**
34133      * Hides this region.
34134      */
34135     hide : function(){
34136         if(!this.collapsed){
34137             this.el.dom.style.left = "-2000px";
34138             this.el.hide();
34139         }else{
34140             this.collapsedEl.dom.style.left = "-2000px";
34141             this.collapsedEl.hide();
34142         }
34143         this.visible = false;
34144         this.fireEvent("visibilitychange", this, false);
34145     },
34146
34147     /**
34148      * Shows this region if it was previously hidden.
34149      */
34150     show : function(){
34151         if(!this.collapsed){
34152             this.el.show();
34153         }else{
34154             this.collapsedEl.show();
34155         }
34156         this.visible = true;
34157         this.fireEvent("visibilitychange", this, true);
34158     },
34159
34160     closeClicked : function(){
34161         if(this.activePanel){
34162             this.remove(this.activePanel);
34163         }
34164     },
34165
34166     collapseClick : function(e){
34167         if(this.isSlid){
34168            e.stopPropagation();
34169            this.slideIn();
34170         }else{
34171            e.stopPropagation();
34172            this.slideOut();
34173         }
34174     },
34175
34176     /**
34177      * Collapses this region.
34178      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34179      */
34180     collapse : function(skipAnim){
34181         if(this.collapsed) {
34182             return;
34183         }
34184         
34185         if(this.fireEvent("beforecollapse", this) != false){
34186             
34187             this.collapsed = true;
34188             if(this.split){
34189                 this.split.el.hide();
34190             }
34191             if(this.config.animate && skipAnim !== true){
34192                 this.fireEvent("invalidated", this);
34193                 this.animateCollapse();
34194             }else{
34195                 this.el.setLocation(-20000,-20000);
34196                 this.el.hide();
34197                 this.collapsedEl.show();
34198                 this.fireEvent("collapsed", this);
34199                 this.fireEvent("invalidated", this);
34200             }
34201         }
34202         
34203     },
34204
34205     animateCollapse : function(){
34206         // overridden
34207     },
34208
34209     /**
34210      * Expands this region if it was previously collapsed.
34211      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34212      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34213      */
34214     expand : function(e, skipAnim){
34215         if(e) {
34216             e.stopPropagation();
34217         }
34218         if(!this.collapsed || this.el.hasActiveFx()) {
34219             return;
34220         }
34221         if(this.isSlid){
34222             this.afterSlideIn();
34223             skipAnim = true;
34224         }
34225         this.collapsed = false;
34226         if(this.config.animate && skipAnim !== true){
34227             this.animateExpand();
34228         }else{
34229             this.el.show();
34230             if(this.split){
34231                 this.split.el.show();
34232             }
34233             this.collapsedEl.setLocation(-2000,-2000);
34234             this.collapsedEl.hide();
34235             this.fireEvent("invalidated", this);
34236             this.fireEvent("expanded", this);
34237         }
34238     },
34239
34240     animateExpand : function(){
34241         // overridden
34242     },
34243
34244     initTabs : function()
34245     {
34246         this.bodyEl.setStyle("overflow", "hidden");
34247         var ts = new Roo.TabPanel(
34248                 this.bodyEl.dom,
34249                 {
34250                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34251                     disableTooltips: this.config.disableTabTips,
34252                     toolbar : this.config.toolbar
34253                 }
34254         );
34255         if(this.config.hideTabs){
34256             ts.stripWrap.setDisplayed(false);
34257         }
34258         this.tabs = ts;
34259         ts.resizeTabs = this.config.resizeTabs === true;
34260         ts.minTabWidth = this.config.minTabWidth || 40;
34261         ts.maxTabWidth = this.config.maxTabWidth || 250;
34262         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34263         ts.monitorResize = false;
34264         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34265         ts.bodyEl.addClass('x-layout-tabs-body');
34266         this.panels.each(this.initPanelAsTab, this);
34267     },
34268
34269     initPanelAsTab : function(panel){
34270         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34271                     this.config.closeOnTab && panel.isClosable());
34272         if(panel.tabTip !== undefined){
34273             ti.setTooltip(panel.tabTip);
34274         }
34275         ti.on("activate", function(){
34276               this.setActivePanel(panel);
34277         }, this);
34278         if(this.config.closeOnTab){
34279             ti.on("beforeclose", function(t, e){
34280                 e.cancel = true;
34281                 this.remove(panel);
34282             }, this);
34283         }
34284         return ti;
34285     },
34286
34287     updatePanelTitle : function(panel, title){
34288         if(this.activePanel == panel){
34289             this.updateTitle(title);
34290         }
34291         if(this.tabs){
34292             var ti = this.tabs.getTab(panel.getEl().id);
34293             ti.setText(title);
34294             if(panel.tabTip !== undefined){
34295                 ti.setTooltip(panel.tabTip);
34296             }
34297         }
34298     },
34299
34300     updateTitle : function(title){
34301         if(this.titleTextEl && !this.config.title){
34302             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34303         }
34304     },
34305
34306     setActivePanel : function(panel){
34307         panel = this.getPanel(panel);
34308         if(this.activePanel && this.activePanel != panel){
34309             this.activePanel.setActiveState(false);
34310         }
34311         this.activePanel = panel;
34312         panel.setActiveState(true);
34313         if(this.panelSize){
34314             panel.setSize(this.panelSize.width, this.panelSize.height);
34315         }
34316         if(this.closeBtn){
34317             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34318         }
34319         this.updateTitle(panel.getTitle());
34320         if(this.tabs){
34321             this.fireEvent("invalidated", this);
34322         }
34323         this.fireEvent("panelactivated", this, panel);
34324     },
34325
34326     /**
34327      * Shows the specified panel.
34328      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34329      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34330      */
34331     showPanel : function(panel)
34332     {
34333         panel = this.getPanel(panel);
34334         if(panel){
34335             if(this.tabs){
34336                 var tab = this.tabs.getTab(panel.getEl().id);
34337                 if(tab.isHidden()){
34338                     this.tabs.unhideTab(tab.id);
34339                 }
34340                 tab.activate();
34341             }else{
34342                 this.setActivePanel(panel);
34343             }
34344         }
34345         return panel;
34346     },
34347
34348     /**
34349      * Get the active panel for this region.
34350      * @return {Roo.ContentPanel} The active panel or null
34351      */
34352     getActivePanel : function(){
34353         return this.activePanel;
34354     },
34355
34356     validateVisibility : function(){
34357         if(this.panels.getCount() < 1){
34358             this.updateTitle("&#160;");
34359             this.closeBtn.hide();
34360             this.hide();
34361         }else{
34362             if(!this.isVisible()){
34363                 this.show();
34364             }
34365         }
34366     },
34367
34368     /**
34369      * Adds the passed ContentPanel(s) to this region.
34370      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34371      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34372      */
34373     add : function(panel){
34374         if(arguments.length > 1){
34375             for(var i = 0, len = arguments.length; i < len; i++) {
34376                 this.add(arguments[i]);
34377             }
34378             return null;
34379         }
34380         if(this.hasPanel(panel)){
34381             this.showPanel(panel);
34382             return panel;
34383         }
34384         panel.setRegion(this);
34385         this.panels.add(panel);
34386         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34387             this.bodyEl.dom.appendChild(panel.getEl().dom);
34388             if(panel.background !== true){
34389                 this.setActivePanel(panel);
34390             }
34391             this.fireEvent("paneladded", this, panel);
34392             return panel;
34393         }
34394         if(!this.tabs){
34395             this.initTabs();
34396         }else{
34397             this.initPanelAsTab(panel);
34398         }
34399         if(panel.background !== true){
34400             this.tabs.activate(panel.getEl().id);
34401         }
34402         this.fireEvent("paneladded", this, panel);
34403         return panel;
34404     },
34405
34406     /**
34407      * Hides the tab for the specified panel.
34408      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34409      */
34410     hidePanel : function(panel){
34411         if(this.tabs && (panel = this.getPanel(panel))){
34412             this.tabs.hideTab(panel.getEl().id);
34413         }
34414     },
34415
34416     /**
34417      * Unhides the tab for a previously hidden panel.
34418      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34419      */
34420     unhidePanel : function(panel){
34421         if(this.tabs && (panel = this.getPanel(panel))){
34422             this.tabs.unhideTab(panel.getEl().id);
34423         }
34424     },
34425
34426     clearPanels : function(){
34427         while(this.panels.getCount() > 0){
34428              this.remove(this.panels.first());
34429         }
34430     },
34431
34432     /**
34433      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34434      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34435      * @param {Boolean} preservePanel Overrides the config preservePanel option
34436      * @return {Roo.ContentPanel} The panel that was removed
34437      */
34438     remove : function(panel, preservePanel){
34439         panel = this.getPanel(panel);
34440         if(!panel){
34441             return null;
34442         }
34443         var e = {};
34444         this.fireEvent("beforeremove", this, panel, e);
34445         if(e.cancel === true){
34446             return null;
34447         }
34448         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34449         var panelId = panel.getId();
34450         this.panels.removeKey(panelId);
34451         if(preservePanel){
34452             document.body.appendChild(panel.getEl().dom);
34453         }
34454         if(this.tabs){
34455             this.tabs.removeTab(panel.getEl().id);
34456         }else if (!preservePanel){
34457             this.bodyEl.dom.removeChild(panel.getEl().dom);
34458         }
34459         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34460             var p = this.panels.first();
34461             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34462             tempEl.appendChild(p.getEl().dom);
34463             this.bodyEl.update("");
34464             this.bodyEl.dom.appendChild(p.getEl().dom);
34465             tempEl = null;
34466             this.updateTitle(p.getTitle());
34467             this.tabs = null;
34468             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34469             this.setActivePanel(p);
34470         }
34471         panel.setRegion(null);
34472         if(this.activePanel == panel){
34473             this.activePanel = null;
34474         }
34475         if(this.config.autoDestroy !== false && preservePanel !== true){
34476             try{panel.destroy();}catch(e){}
34477         }
34478         this.fireEvent("panelremoved", this, panel);
34479         return panel;
34480     },
34481
34482     /**
34483      * Returns the TabPanel component used by this region
34484      * @return {Roo.TabPanel}
34485      */
34486     getTabs : function(){
34487         return this.tabs;
34488     },
34489
34490     createTool : function(parentEl, className){
34491         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34492             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34493         btn.addClassOnOver("x-layout-tools-button-over");
34494         return btn;
34495     }
34496 });/*
34497  * Based on:
34498  * Ext JS Library 1.1.1
34499  * Copyright(c) 2006-2007, Ext JS, LLC.
34500  *
34501  * Originally Released Under LGPL - original licence link has changed is not relivant.
34502  *
34503  * Fork - LGPL
34504  * <script type="text/javascript">
34505  */
34506  
34507
34508
34509 /**
34510  * @class Roo.SplitLayoutRegion
34511  * @extends Roo.LayoutRegion
34512  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34513  */
34514 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34515     this.cursor = cursor;
34516     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34517 };
34518
34519 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34520     splitTip : "Drag to resize.",
34521     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34522     useSplitTips : false,
34523
34524     applyConfig : function(config){
34525         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34526         if(config.split){
34527             if(!this.split){
34528                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34529                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34530                 /** The SplitBar for this region 
34531                 * @type Roo.SplitBar */
34532                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34533                 this.split.on("moved", this.onSplitMove, this);
34534                 this.split.useShim = config.useShim === true;
34535                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34536                 if(this.useSplitTips){
34537                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34538                 }
34539                 if(config.collapsible){
34540                     this.split.el.on("dblclick", this.collapse,  this);
34541                 }
34542             }
34543             if(typeof config.minSize != "undefined"){
34544                 this.split.minSize = config.minSize;
34545             }
34546             if(typeof config.maxSize != "undefined"){
34547                 this.split.maxSize = config.maxSize;
34548             }
34549             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34550                 this.hideSplitter();
34551             }
34552         }
34553     },
34554
34555     getHMaxSize : function(){
34556          var cmax = this.config.maxSize || 10000;
34557          var center = this.mgr.getRegion("center");
34558          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34559     },
34560
34561     getVMaxSize : function(){
34562          var cmax = this.config.maxSize || 10000;
34563          var center = this.mgr.getRegion("center");
34564          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34565     },
34566
34567     onSplitMove : function(split, newSize){
34568         this.fireEvent("resized", this, newSize);
34569     },
34570     
34571     /** 
34572      * Returns the {@link Roo.SplitBar} for this region.
34573      * @return {Roo.SplitBar}
34574      */
34575     getSplitBar : function(){
34576         return this.split;
34577     },
34578     
34579     hide : function(){
34580         this.hideSplitter();
34581         Roo.SplitLayoutRegion.superclass.hide.call(this);
34582     },
34583
34584     hideSplitter : function(){
34585         if(this.split){
34586             this.split.el.setLocation(-2000,-2000);
34587             this.split.el.hide();
34588         }
34589     },
34590
34591     show : function(){
34592         if(this.split){
34593             this.split.el.show();
34594         }
34595         Roo.SplitLayoutRegion.superclass.show.call(this);
34596     },
34597     
34598     beforeSlide: function(){
34599         if(Roo.isGecko){// firefox overflow auto bug workaround
34600             this.bodyEl.clip();
34601             if(this.tabs) {
34602                 this.tabs.bodyEl.clip();
34603             }
34604             if(this.activePanel){
34605                 this.activePanel.getEl().clip();
34606                 
34607                 if(this.activePanel.beforeSlide){
34608                     this.activePanel.beforeSlide();
34609                 }
34610             }
34611         }
34612     },
34613     
34614     afterSlide : function(){
34615         if(Roo.isGecko){// firefox overflow auto bug workaround
34616             this.bodyEl.unclip();
34617             if(this.tabs) {
34618                 this.tabs.bodyEl.unclip();
34619             }
34620             if(this.activePanel){
34621                 this.activePanel.getEl().unclip();
34622                 if(this.activePanel.afterSlide){
34623                     this.activePanel.afterSlide();
34624                 }
34625             }
34626         }
34627     },
34628
34629     initAutoHide : function(){
34630         if(this.autoHide !== false){
34631             if(!this.autoHideHd){
34632                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34633                 this.autoHideHd = {
34634                     "mouseout": function(e){
34635                         if(!e.within(this.el, true)){
34636                             st.delay(500);
34637                         }
34638                     },
34639                     "mouseover" : function(e){
34640                         st.cancel();
34641                     },
34642                     scope : this
34643                 };
34644             }
34645             this.el.on(this.autoHideHd);
34646         }
34647     },
34648
34649     clearAutoHide : function(){
34650         if(this.autoHide !== false){
34651             this.el.un("mouseout", this.autoHideHd.mouseout);
34652             this.el.un("mouseover", this.autoHideHd.mouseover);
34653         }
34654     },
34655
34656     clearMonitor : function(){
34657         Roo.get(document).un("click", this.slideInIf, this);
34658     },
34659
34660     // these names are backwards but not changed for compat
34661     slideOut : function(){
34662         if(this.isSlid || this.el.hasActiveFx()){
34663             return;
34664         }
34665         this.isSlid = true;
34666         if(this.collapseBtn){
34667             this.collapseBtn.hide();
34668         }
34669         this.closeBtnState = this.closeBtn.getStyle('display');
34670         this.closeBtn.hide();
34671         if(this.stickBtn){
34672             this.stickBtn.show();
34673         }
34674         this.el.show();
34675         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34676         this.beforeSlide();
34677         this.el.setStyle("z-index", 10001);
34678         this.el.slideIn(this.getSlideAnchor(), {
34679             callback: function(){
34680                 this.afterSlide();
34681                 this.initAutoHide();
34682                 Roo.get(document).on("click", this.slideInIf, this);
34683                 this.fireEvent("slideshow", this);
34684             },
34685             scope: this,
34686             block: true
34687         });
34688     },
34689
34690     afterSlideIn : function(){
34691         this.clearAutoHide();
34692         this.isSlid = false;
34693         this.clearMonitor();
34694         this.el.setStyle("z-index", "");
34695         if(this.collapseBtn){
34696             this.collapseBtn.show();
34697         }
34698         this.closeBtn.setStyle('display', this.closeBtnState);
34699         if(this.stickBtn){
34700             this.stickBtn.hide();
34701         }
34702         this.fireEvent("slidehide", this);
34703     },
34704
34705     slideIn : function(cb){
34706         if(!this.isSlid || this.el.hasActiveFx()){
34707             Roo.callback(cb);
34708             return;
34709         }
34710         this.isSlid = false;
34711         this.beforeSlide();
34712         this.el.slideOut(this.getSlideAnchor(), {
34713             callback: function(){
34714                 this.el.setLeftTop(-10000, -10000);
34715                 this.afterSlide();
34716                 this.afterSlideIn();
34717                 Roo.callback(cb);
34718             },
34719             scope: this,
34720             block: true
34721         });
34722     },
34723     
34724     slideInIf : function(e){
34725         if(!e.within(this.el)){
34726             this.slideIn();
34727         }
34728     },
34729
34730     animateCollapse : function(){
34731         this.beforeSlide();
34732         this.el.setStyle("z-index", 20000);
34733         var anchor = this.getSlideAnchor();
34734         this.el.slideOut(anchor, {
34735             callback : function(){
34736                 this.el.setStyle("z-index", "");
34737                 this.collapsedEl.slideIn(anchor, {duration:.3});
34738                 this.afterSlide();
34739                 this.el.setLocation(-10000,-10000);
34740                 this.el.hide();
34741                 this.fireEvent("collapsed", this);
34742             },
34743             scope: this,
34744             block: true
34745         });
34746     },
34747
34748     animateExpand : function(){
34749         this.beforeSlide();
34750         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34751         this.el.setStyle("z-index", 20000);
34752         this.collapsedEl.hide({
34753             duration:.1
34754         });
34755         this.el.slideIn(this.getSlideAnchor(), {
34756             callback : function(){
34757                 this.el.setStyle("z-index", "");
34758                 this.afterSlide();
34759                 if(this.split){
34760                     this.split.el.show();
34761                 }
34762                 this.fireEvent("invalidated", this);
34763                 this.fireEvent("expanded", this);
34764             },
34765             scope: this,
34766             block: true
34767         });
34768     },
34769
34770     anchors : {
34771         "west" : "left",
34772         "east" : "right",
34773         "north" : "top",
34774         "south" : "bottom"
34775     },
34776
34777     sanchors : {
34778         "west" : "l",
34779         "east" : "r",
34780         "north" : "t",
34781         "south" : "b"
34782     },
34783
34784     canchors : {
34785         "west" : "tl-tr",
34786         "east" : "tr-tl",
34787         "north" : "tl-bl",
34788         "south" : "bl-tl"
34789     },
34790
34791     getAnchor : function(){
34792         return this.anchors[this.position];
34793     },
34794
34795     getCollapseAnchor : function(){
34796         return this.canchors[this.position];
34797     },
34798
34799     getSlideAnchor : function(){
34800         return this.sanchors[this.position];
34801     },
34802
34803     getAlignAdj : function(){
34804         var cm = this.cmargins;
34805         switch(this.position){
34806             case "west":
34807                 return [0, 0];
34808             break;
34809             case "east":
34810                 return [0, 0];
34811             break;
34812             case "north":
34813                 return [0, 0];
34814             break;
34815             case "south":
34816                 return [0, 0];
34817             break;
34818         }
34819     },
34820
34821     getExpandAdj : function(){
34822         var c = this.collapsedEl, cm = this.cmargins;
34823         switch(this.position){
34824             case "west":
34825                 return [-(cm.right+c.getWidth()+cm.left), 0];
34826             break;
34827             case "east":
34828                 return [cm.right+c.getWidth()+cm.left, 0];
34829             break;
34830             case "north":
34831                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34832             break;
34833             case "south":
34834                 return [0, cm.top+cm.bottom+c.getHeight()];
34835             break;
34836         }
34837     }
34838 });/*
34839  * Based on:
34840  * Ext JS Library 1.1.1
34841  * Copyright(c) 2006-2007, Ext JS, LLC.
34842  *
34843  * Originally Released Under LGPL - original licence link has changed is not relivant.
34844  *
34845  * Fork - LGPL
34846  * <script type="text/javascript">
34847  */
34848 /*
34849  * These classes are private internal classes
34850  */
34851 Roo.CenterLayoutRegion = function(mgr, config){
34852     Roo.LayoutRegion.call(this, mgr, config, "center");
34853     this.visible = true;
34854     this.minWidth = config.minWidth || 20;
34855     this.minHeight = config.minHeight || 20;
34856 };
34857
34858 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34859     hide : function(){
34860         // center panel can't be hidden
34861     },
34862     
34863     show : function(){
34864         // center panel can't be hidden
34865     },
34866     
34867     getMinWidth: function(){
34868         return this.minWidth;
34869     },
34870     
34871     getMinHeight: function(){
34872         return this.minHeight;
34873     }
34874 });
34875
34876
34877 Roo.NorthLayoutRegion = function(mgr, config){
34878     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34879     if(this.split){
34880         this.split.placement = Roo.SplitBar.TOP;
34881         this.split.orientation = Roo.SplitBar.VERTICAL;
34882         this.split.el.addClass("x-layout-split-v");
34883     }
34884     var size = config.initialSize || config.height;
34885     if(typeof size != "undefined"){
34886         this.el.setHeight(size);
34887     }
34888 };
34889 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34890     orientation: Roo.SplitBar.VERTICAL,
34891     getBox : function(){
34892         if(this.collapsed){
34893             return this.collapsedEl.getBox();
34894         }
34895         var box = this.el.getBox();
34896         if(this.split){
34897             box.height += this.split.el.getHeight();
34898         }
34899         return box;
34900     },
34901     
34902     updateBox : function(box){
34903         if(this.split && !this.collapsed){
34904             box.height -= this.split.el.getHeight();
34905             this.split.el.setLeft(box.x);
34906             this.split.el.setTop(box.y+box.height);
34907             this.split.el.setWidth(box.width);
34908         }
34909         if(this.collapsed){
34910             this.updateBody(box.width, null);
34911         }
34912         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34913     }
34914 });
34915
34916 Roo.SouthLayoutRegion = function(mgr, config){
34917     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34918     if(this.split){
34919         this.split.placement = Roo.SplitBar.BOTTOM;
34920         this.split.orientation = Roo.SplitBar.VERTICAL;
34921         this.split.el.addClass("x-layout-split-v");
34922     }
34923     var size = config.initialSize || config.height;
34924     if(typeof size != "undefined"){
34925         this.el.setHeight(size);
34926     }
34927 };
34928 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34929     orientation: Roo.SplitBar.VERTICAL,
34930     getBox : function(){
34931         if(this.collapsed){
34932             return this.collapsedEl.getBox();
34933         }
34934         var box = this.el.getBox();
34935         if(this.split){
34936             var sh = this.split.el.getHeight();
34937             box.height += sh;
34938             box.y -= sh;
34939         }
34940         return box;
34941     },
34942     
34943     updateBox : function(box){
34944         if(this.split && !this.collapsed){
34945             var sh = this.split.el.getHeight();
34946             box.height -= sh;
34947             box.y += sh;
34948             this.split.el.setLeft(box.x);
34949             this.split.el.setTop(box.y-sh);
34950             this.split.el.setWidth(box.width);
34951         }
34952         if(this.collapsed){
34953             this.updateBody(box.width, null);
34954         }
34955         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34956     }
34957 });
34958
34959 Roo.EastLayoutRegion = function(mgr, config){
34960     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34961     if(this.split){
34962         this.split.placement = Roo.SplitBar.RIGHT;
34963         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34964         this.split.el.addClass("x-layout-split-h");
34965     }
34966     var size = config.initialSize || config.width;
34967     if(typeof size != "undefined"){
34968         this.el.setWidth(size);
34969     }
34970 };
34971 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34972     orientation: Roo.SplitBar.HORIZONTAL,
34973     getBox : function(){
34974         if(this.collapsed){
34975             return this.collapsedEl.getBox();
34976         }
34977         var box = this.el.getBox();
34978         if(this.split){
34979             var sw = this.split.el.getWidth();
34980             box.width += sw;
34981             box.x -= sw;
34982         }
34983         return box;
34984     },
34985
34986     updateBox : function(box){
34987         if(this.split && !this.collapsed){
34988             var sw = this.split.el.getWidth();
34989             box.width -= sw;
34990             this.split.el.setLeft(box.x);
34991             this.split.el.setTop(box.y);
34992             this.split.el.setHeight(box.height);
34993             box.x += sw;
34994         }
34995         if(this.collapsed){
34996             this.updateBody(null, box.height);
34997         }
34998         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34999     }
35000 });
35001
35002 Roo.WestLayoutRegion = function(mgr, config){
35003     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35004     if(this.split){
35005         this.split.placement = Roo.SplitBar.LEFT;
35006         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35007         this.split.el.addClass("x-layout-split-h");
35008     }
35009     var size = config.initialSize || config.width;
35010     if(typeof size != "undefined"){
35011         this.el.setWidth(size);
35012     }
35013 };
35014 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35015     orientation: Roo.SplitBar.HORIZONTAL,
35016     getBox : function(){
35017         if(this.collapsed){
35018             return this.collapsedEl.getBox();
35019         }
35020         var box = this.el.getBox();
35021         if(this.split){
35022             box.width += this.split.el.getWidth();
35023         }
35024         return box;
35025     },
35026     
35027     updateBox : function(box){
35028         if(this.split && !this.collapsed){
35029             var sw = this.split.el.getWidth();
35030             box.width -= sw;
35031             this.split.el.setLeft(box.x+box.width);
35032             this.split.el.setTop(box.y);
35033             this.split.el.setHeight(box.height);
35034         }
35035         if(this.collapsed){
35036             this.updateBody(null, box.height);
35037         }
35038         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35039     }
35040 });
35041 /*
35042  * Based on:
35043  * Ext JS Library 1.1.1
35044  * Copyright(c) 2006-2007, Ext JS, LLC.
35045  *
35046  * Originally Released Under LGPL - original licence link has changed is not relivant.
35047  *
35048  * Fork - LGPL
35049  * <script type="text/javascript">
35050  */
35051  
35052  
35053 /*
35054  * Private internal class for reading and applying state
35055  */
35056 Roo.LayoutStateManager = function(layout){
35057      // default empty state
35058      this.state = {
35059         north: {},
35060         south: {},
35061         east: {},
35062         west: {}       
35063     };
35064 };
35065
35066 Roo.LayoutStateManager.prototype = {
35067     init : function(layout, provider){
35068         this.provider = provider;
35069         var state = provider.get(layout.id+"-layout-state");
35070         if(state){
35071             var wasUpdating = layout.isUpdating();
35072             if(!wasUpdating){
35073                 layout.beginUpdate();
35074             }
35075             for(var key in state){
35076                 if(typeof state[key] != "function"){
35077                     var rstate = state[key];
35078                     var r = layout.getRegion(key);
35079                     if(r && rstate){
35080                         if(rstate.size){
35081                             r.resizeTo(rstate.size);
35082                         }
35083                         if(rstate.collapsed == true){
35084                             r.collapse(true);
35085                         }else{
35086                             r.expand(null, true);
35087                         }
35088                     }
35089                 }
35090             }
35091             if(!wasUpdating){
35092                 layout.endUpdate();
35093             }
35094             this.state = state; 
35095         }
35096         this.layout = layout;
35097         layout.on("regionresized", this.onRegionResized, this);
35098         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35099         layout.on("regionexpanded", this.onRegionExpanded, this);
35100     },
35101     
35102     storeState : function(){
35103         this.provider.set(this.layout.id+"-layout-state", this.state);
35104     },
35105     
35106     onRegionResized : function(region, newSize){
35107         this.state[region.getPosition()].size = newSize;
35108         this.storeState();
35109     },
35110     
35111     onRegionCollapsed : function(region){
35112         this.state[region.getPosition()].collapsed = true;
35113         this.storeState();
35114     },
35115     
35116     onRegionExpanded : function(region){
35117         this.state[region.getPosition()].collapsed = false;
35118         this.storeState();
35119     }
35120 };/*
35121  * Based on:
35122  * Ext JS Library 1.1.1
35123  * Copyright(c) 2006-2007, Ext JS, LLC.
35124  *
35125  * Originally Released Under LGPL - original licence link has changed is not relivant.
35126  *
35127  * Fork - LGPL
35128  * <script type="text/javascript">
35129  */
35130 /**
35131  * @class Roo.ContentPanel
35132  * @extends Roo.util.Observable
35133  * A basic ContentPanel element.
35134  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35135  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35136  * @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
35137  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35138  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35139  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35140  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35141  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35142  * @cfg {String} title          The title for this panel
35143  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35144  * @cfg {String} url            Calls {@link #setUrl} with this value
35145  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35146  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35147  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35148  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35149
35150  * @constructor
35151  * Create a new ContentPanel.
35152  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35153  * @param {String/Object} config A string to set only the title or a config object
35154  * @param {String} content (optional) Set the HTML content for this panel
35155  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35156  */
35157 Roo.ContentPanel = function(el, config, content){
35158     
35159      
35160     /*
35161     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35162         config = el;
35163         el = Roo.id();
35164     }
35165     if (config && config.parentLayout) { 
35166         el = config.parentLayout.el.createChild(); 
35167     }
35168     */
35169     if(el.autoCreate){ // xtype is available if this is called from factory
35170         config = el;
35171         el = Roo.id();
35172     }
35173     this.el = Roo.get(el);
35174     if(!this.el && config && config.autoCreate){
35175         if(typeof config.autoCreate == "object"){
35176             if(!config.autoCreate.id){
35177                 config.autoCreate.id = config.id||el;
35178             }
35179             this.el = Roo.DomHelper.append(document.body,
35180                         config.autoCreate, true);
35181         }else{
35182             this.el = Roo.DomHelper.append(document.body,
35183                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35184         }
35185     }
35186     this.closable = false;
35187     this.loaded = false;
35188     this.active = false;
35189     if(typeof config == "string"){
35190         this.title = config;
35191     }else{
35192         Roo.apply(this, config);
35193     }
35194     
35195     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35196         this.wrapEl = this.el.wrap();
35197         this.toolbar.container = this.el.insertSibling(false, 'before');
35198         this.toolbar = new Roo.Toolbar(this.toolbar);
35199     }
35200     
35201     // xtype created footer. - not sure if will work as we normally have to render first..
35202     if (this.footer && !this.footer.el && this.footer.xtype) {
35203         if (!this.wrapEl) {
35204             this.wrapEl = this.el.wrap();
35205         }
35206     
35207         this.footer.container = this.wrapEl.createChild();
35208          
35209         this.footer = Roo.factory(this.footer, Roo);
35210         
35211     }
35212     
35213     if(this.resizeEl){
35214         this.resizeEl = Roo.get(this.resizeEl, true);
35215     }else{
35216         this.resizeEl = this.el;
35217     }
35218     // handle view.xtype
35219     
35220  
35221     
35222     
35223     this.addEvents({
35224         /**
35225          * @event activate
35226          * Fires when this panel is activated. 
35227          * @param {Roo.ContentPanel} this
35228          */
35229         "activate" : true,
35230         /**
35231          * @event deactivate
35232          * Fires when this panel is activated. 
35233          * @param {Roo.ContentPanel} this
35234          */
35235         "deactivate" : true,
35236
35237         /**
35238          * @event resize
35239          * Fires when this panel is resized if fitToFrame is true.
35240          * @param {Roo.ContentPanel} this
35241          * @param {Number} width The width after any component adjustments
35242          * @param {Number} height The height after any component adjustments
35243          */
35244         "resize" : true,
35245         
35246          /**
35247          * @event render
35248          * Fires when this tab is created
35249          * @param {Roo.ContentPanel} this
35250          */
35251         "render" : true
35252         
35253         
35254         
35255     });
35256     
35257
35258     
35259     
35260     if(this.autoScroll){
35261         this.resizeEl.setStyle("overflow", "auto");
35262     } else {
35263         // fix randome scrolling
35264         this.el.on('scroll', function() {
35265             Roo.log('fix random scolling');
35266             this.scrollTo('top',0); 
35267         });
35268     }
35269     content = content || this.content;
35270     if(content){
35271         this.setContent(content);
35272     }
35273     if(config && config.url){
35274         this.setUrl(this.url, this.params, this.loadOnce);
35275     }
35276     
35277     
35278     
35279     Roo.ContentPanel.superclass.constructor.call(this);
35280     
35281     if (this.view && typeof(this.view.xtype) != 'undefined') {
35282         this.view.el = this.el.appendChild(document.createElement("div"));
35283         this.view = Roo.factory(this.view); 
35284         this.view.render  &&  this.view.render(false, '');  
35285     }
35286     
35287     
35288     this.fireEvent('render', this);
35289 };
35290
35291 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35292     tabTip:'',
35293     setRegion : function(region){
35294         this.region = region;
35295         if(region){
35296            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35297         }else{
35298            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35299         } 
35300     },
35301     
35302     /**
35303      * Returns the toolbar for this Panel if one was configured. 
35304      * @return {Roo.Toolbar} 
35305      */
35306     getToolbar : function(){
35307         return this.toolbar;
35308     },
35309     
35310     setActiveState : function(active){
35311         this.active = active;
35312         if(!active){
35313             this.fireEvent("deactivate", this);
35314         }else{
35315             this.fireEvent("activate", this);
35316         }
35317     },
35318     /**
35319      * Updates this panel's element
35320      * @param {String} content The new content
35321      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35322     */
35323     setContent : function(content, loadScripts){
35324         this.el.update(content, loadScripts);
35325     },
35326
35327     ignoreResize : function(w, h){
35328         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35329             return true;
35330         }else{
35331             this.lastSize = {width: w, height: h};
35332             return false;
35333         }
35334     },
35335     /**
35336      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35337      * @return {Roo.UpdateManager} The UpdateManager
35338      */
35339     getUpdateManager : function(){
35340         return this.el.getUpdateManager();
35341     },
35342      /**
35343      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35344      * @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:
35345 <pre><code>
35346 panel.load({
35347     url: "your-url.php",
35348     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35349     callback: yourFunction,
35350     scope: yourObject, //(optional scope)
35351     discardUrl: false,
35352     nocache: false,
35353     text: "Loading...",
35354     timeout: 30,
35355     scripts: false
35356 });
35357 </code></pre>
35358      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35359      * 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.
35360      * @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}
35361      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35362      * @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.
35363      * @return {Roo.ContentPanel} this
35364      */
35365     load : function(){
35366         var um = this.el.getUpdateManager();
35367         um.update.apply(um, arguments);
35368         return this;
35369     },
35370
35371
35372     /**
35373      * 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.
35374      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35375      * @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)
35376      * @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)
35377      * @return {Roo.UpdateManager} The UpdateManager
35378      */
35379     setUrl : function(url, params, loadOnce){
35380         if(this.refreshDelegate){
35381             this.removeListener("activate", this.refreshDelegate);
35382         }
35383         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35384         this.on("activate", this.refreshDelegate);
35385         return this.el.getUpdateManager();
35386     },
35387     
35388     _handleRefresh : function(url, params, loadOnce){
35389         if(!loadOnce || !this.loaded){
35390             var updater = this.el.getUpdateManager();
35391             updater.update(url, params, this._setLoaded.createDelegate(this));
35392         }
35393     },
35394     
35395     _setLoaded : function(){
35396         this.loaded = true;
35397     }, 
35398     
35399     /**
35400      * Returns this panel's id
35401      * @return {String} 
35402      */
35403     getId : function(){
35404         return this.el.id;
35405     },
35406     
35407     /** 
35408      * Returns this panel's element - used by regiosn to add.
35409      * @return {Roo.Element} 
35410      */
35411     getEl : function(){
35412         return this.wrapEl || this.el;
35413     },
35414     
35415     adjustForComponents : function(width, height)
35416     {
35417         //Roo.log('adjustForComponents ');
35418         if(this.resizeEl != this.el){
35419             width -= this.el.getFrameWidth('lr');
35420             height -= this.el.getFrameWidth('tb');
35421         }
35422         if(this.toolbar){
35423             var te = this.toolbar.getEl();
35424             height -= te.getHeight();
35425             te.setWidth(width);
35426         }
35427         if(this.footer){
35428             var te = this.footer.getEl();
35429             Roo.log("footer:" + te.getHeight());
35430             
35431             height -= te.getHeight();
35432             te.setWidth(width);
35433         }
35434         
35435         
35436         if(this.adjustments){
35437             width += this.adjustments[0];
35438             height += this.adjustments[1];
35439         }
35440         return {"width": width, "height": height};
35441     },
35442     
35443     setSize : function(width, height){
35444         if(this.fitToFrame && !this.ignoreResize(width, height)){
35445             if(this.fitContainer && this.resizeEl != this.el){
35446                 this.el.setSize(width, height);
35447             }
35448             var size = this.adjustForComponents(width, height);
35449             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35450             this.fireEvent('resize', this, size.width, size.height);
35451         }
35452     },
35453     
35454     /**
35455      * Returns this panel's title
35456      * @return {String} 
35457      */
35458     getTitle : function(){
35459         return this.title;
35460     },
35461     
35462     /**
35463      * Set this panel's title
35464      * @param {String} title
35465      */
35466     setTitle : function(title){
35467         this.title = title;
35468         if(this.region){
35469             this.region.updatePanelTitle(this, title);
35470         }
35471     },
35472     
35473     /**
35474      * Returns true is this panel was configured to be closable
35475      * @return {Boolean} 
35476      */
35477     isClosable : function(){
35478         return this.closable;
35479     },
35480     
35481     beforeSlide : function(){
35482         this.el.clip();
35483         this.resizeEl.clip();
35484     },
35485     
35486     afterSlide : function(){
35487         this.el.unclip();
35488         this.resizeEl.unclip();
35489     },
35490     
35491     /**
35492      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35493      *   Will fail silently if the {@link #setUrl} method has not been called.
35494      *   This does not activate the panel, just updates its content.
35495      */
35496     refresh : function(){
35497         if(this.refreshDelegate){
35498            this.loaded = false;
35499            this.refreshDelegate();
35500         }
35501     },
35502     
35503     /**
35504      * Destroys this panel
35505      */
35506     destroy : function(){
35507         this.el.removeAllListeners();
35508         var tempEl = document.createElement("span");
35509         tempEl.appendChild(this.el.dom);
35510         tempEl.innerHTML = "";
35511         this.el.remove();
35512         this.el = null;
35513     },
35514     
35515     /**
35516      * form - if the content panel contains a form - this is a reference to it.
35517      * @type {Roo.form.Form}
35518      */
35519     form : false,
35520     /**
35521      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35522      *    This contains a reference to it.
35523      * @type {Roo.View}
35524      */
35525     view : false,
35526     
35527       /**
35528      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35529      * <pre><code>
35530
35531 layout.addxtype({
35532        xtype : 'Form',
35533        items: [ .... ]
35534    }
35535 );
35536
35537 </code></pre>
35538      * @param {Object} cfg Xtype definition of item to add.
35539      */
35540     
35541     addxtype : function(cfg) {
35542         // add form..
35543         if (cfg.xtype.match(/^Form$/)) {
35544             
35545             var el;
35546             //if (this.footer) {
35547             //    el = this.footer.container.insertSibling(false, 'before');
35548             //} else {
35549                 el = this.el.createChild();
35550             //}
35551
35552             this.form = new  Roo.form.Form(cfg);
35553             
35554             
35555             if ( this.form.allItems.length) {
35556                 this.form.render(el.dom);
35557             }
35558             return this.form;
35559         }
35560         // should only have one of theses..
35561         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35562             // views.. should not be just added - used named prop 'view''
35563             
35564             cfg.el = this.el.appendChild(document.createElement("div"));
35565             // factory?
35566             
35567             var ret = new Roo.factory(cfg);
35568              
35569              ret.render && ret.render(false, ''); // render blank..
35570             this.view = ret;
35571             return ret;
35572         }
35573         return false;
35574     }
35575 });
35576
35577 /**
35578  * @class Roo.GridPanel
35579  * @extends Roo.ContentPanel
35580  * @constructor
35581  * Create a new GridPanel.
35582  * @param {Roo.grid.Grid} grid The grid for this panel
35583  * @param {String/Object} config A string to set only the panel's title, or a config object
35584  */
35585 Roo.GridPanel = function(grid, config){
35586     
35587   
35588     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35589         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35590         
35591     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35592     
35593     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35594     
35595     if(this.toolbar){
35596         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35597     }
35598     // xtype created footer. - not sure if will work as we normally have to render first..
35599     if (this.footer && !this.footer.el && this.footer.xtype) {
35600         
35601         this.footer.container = this.grid.getView().getFooterPanel(true);
35602         this.footer.dataSource = this.grid.dataSource;
35603         this.footer = Roo.factory(this.footer, Roo);
35604         
35605     }
35606     
35607     grid.monitorWindowResize = false; // turn off autosizing
35608     grid.autoHeight = false;
35609     grid.autoWidth = false;
35610     this.grid = grid;
35611     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35612 };
35613
35614 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35615     getId : function(){
35616         return this.grid.id;
35617     },
35618     
35619     /**
35620      * Returns the grid for this panel
35621      * @return {Roo.grid.Grid} 
35622      */
35623     getGrid : function(){
35624         return this.grid;    
35625     },
35626     
35627     setSize : function(width, height){
35628         if(!this.ignoreResize(width, height)){
35629             var grid = this.grid;
35630             var size = this.adjustForComponents(width, height);
35631             grid.getGridEl().setSize(size.width, size.height);
35632             grid.autoSize();
35633         }
35634     },
35635     
35636     beforeSlide : function(){
35637         this.grid.getView().scroller.clip();
35638     },
35639     
35640     afterSlide : function(){
35641         this.grid.getView().scroller.unclip();
35642     },
35643     
35644     destroy : function(){
35645         this.grid.destroy();
35646         delete this.grid;
35647         Roo.GridPanel.superclass.destroy.call(this); 
35648     }
35649 });
35650
35651
35652 /**
35653  * @class Roo.NestedLayoutPanel
35654  * @extends Roo.ContentPanel
35655  * @constructor
35656  * Create a new NestedLayoutPanel.
35657  * 
35658  * 
35659  * @param {Roo.BorderLayout} layout The layout for this panel
35660  * @param {String/Object} config A string to set only the title or a config object
35661  */
35662 Roo.NestedLayoutPanel = function(layout, config)
35663 {
35664     // construct with only one argument..
35665     /* FIXME - implement nicer consturctors
35666     if (layout.layout) {
35667         config = layout;
35668         layout = config.layout;
35669         delete config.layout;
35670     }
35671     if (layout.xtype && !layout.getEl) {
35672         // then layout needs constructing..
35673         layout = Roo.factory(layout, Roo);
35674     }
35675     */
35676     
35677     
35678     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35679     
35680     layout.monitorWindowResize = false; // turn off autosizing
35681     this.layout = layout;
35682     this.layout.getEl().addClass("x-layout-nested-layout");
35683     
35684     
35685     
35686     
35687 };
35688
35689 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35690
35691     setSize : function(width, height){
35692         if(!this.ignoreResize(width, height)){
35693             var size = this.adjustForComponents(width, height);
35694             var el = this.layout.getEl();
35695             el.setSize(size.width, size.height);
35696             var touch = el.dom.offsetWidth;
35697             this.layout.layout();
35698             // ie requires a double layout on the first pass
35699             if(Roo.isIE && !this.initialized){
35700                 this.initialized = true;
35701                 this.layout.layout();
35702             }
35703         }
35704     },
35705     
35706     // activate all subpanels if not currently active..
35707     
35708     setActiveState : function(active){
35709         this.active = active;
35710         if(!active){
35711             this.fireEvent("deactivate", this);
35712             return;
35713         }
35714         
35715         this.fireEvent("activate", this);
35716         // not sure if this should happen before or after..
35717         if (!this.layout) {
35718             return; // should not happen..
35719         }
35720         var reg = false;
35721         for (var r in this.layout.regions) {
35722             reg = this.layout.getRegion(r);
35723             if (reg.getActivePanel()) {
35724                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35725                 reg.setActivePanel(reg.getActivePanel());
35726                 continue;
35727             }
35728             if (!reg.panels.length) {
35729                 continue;
35730             }
35731             reg.showPanel(reg.getPanel(0));
35732         }
35733         
35734         
35735         
35736         
35737     },
35738     
35739     /**
35740      * Returns the nested BorderLayout for this panel
35741      * @return {Roo.BorderLayout} 
35742      */
35743     getLayout : function(){
35744         return this.layout;
35745     },
35746     
35747      /**
35748      * Adds a xtype elements to the layout of the nested panel
35749      * <pre><code>
35750
35751 panel.addxtype({
35752        xtype : 'ContentPanel',
35753        region: 'west',
35754        items: [ .... ]
35755    }
35756 );
35757
35758 panel.addxtype({
35759         xtype : 'NestedLayoutPanel',
35760         region: 'west',
35761         layout: {
35762            center: { },
35763            west: { }   
35764         },
35765         items : [ ... list of content panels or nested layout panels.. ]
35766    }
35767 );
35768 </code></pre>
35769      * @param {Object} cfg Xtype definition of item to add.
35770      */
35771     addxtype : function(cfg) {
35772         return this.layout.addxtype(cfg);
35773     
35774     }
35775 });
35776
35777 Roo.ScrollPanel = function(el, config, content){
35778     config = config || {};
35779     config.fitToFrame = true;
35780     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35781     
35782     this.el.dom.style.overflow = "hidden";
35783     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35784     this.el.removeClass("x-layout-inactive-content");
35785     this.el.on("mousewheel", this.onWheel, this);
35786
35787     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35788     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35789     up.unselectable(); down.unselectable();
35790     up.on("click", this.scrollUp, this);
35791     down.on("click", this.scrollDown, this);
35792     up.addClassOnOver("x-scroller-btn-over");
35793     down.addClassOnOver("x-scroller-btn-over");
35794     up.addClassOnClick("x-scroller-btn-click");
35795     down.addClassOnClick("x-scroller-btn-click");
35796     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35797
35798     this.resizeEl = this.el;
35799     this.el = wrap; this.up = up; this.down = down;
35800 };
35801
35802 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35803     increment : 100,
35804     wheelIncrement : 5,
35805     scrollUp : function(){
35806         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35807     },
35808
35809     scrollDown : function(){
35810         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35811     },
35812
35813     afterScroll : function(){
35814         var el = this.resizeEl;
35815         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35816         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35817         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35818     },
35819
35820     setSize : function(){
35821         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35822         this.afterScroll();
35823     },
35824
35825     onWheel : function(e){
35826         var d = e.getWheelDelta();
35827         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35828         this.afterScroll();
35829         e.stopEvent();
35830     },
35831
35832     setContent : function(content, loadScripts){
35833         this.resizeEl.update(content, loadScripts);
35834     }
35835
35836 });
35837
35838
35839
35840
35841
35842
35843
35844
35845
35846 /**
35847  * @class Roo.TreePanel
35848  * @extends Roo.ContentPanel
35849  * @constructor
35850  * Create a new TreePanel. - defaults to fit/scoll contents.
35851  * @param {String/Object} config A string to set only the panel's title, or a config object
35852  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35853  */
35854 Roo.TreePanel = function(config){
35855     var el = config.el;
35856     var tree = config.tree;
35857     delete config.tree; 
35858     delete config.el; // hopefull!
35859     
35860     // wrapper for IE7 strict & safari scroll issue
35861     
35862     var treeEl = el.createChild();
35863     config.resizeEl = treeEl;
35864     
35865     
35866     
35867     Roo.TreePanel.superclass.constructor.call(this, el, config);
35868  
35869  
35870     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35871     //console.log(tree);
35872     this.on('activate', function()
35873     {
35874         if (this.tree.rendered) {
35875             return;
35876         }
35877         //console.log('render tree');
35878         this.tree.render();
35879     });
35880     // this should not be needed.. - it's actually the 'el' that resizes?
35881     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35882     
35883     //this.on('resize',  function (cp, w, h) {
35884     //        this.tree.innerCt.setWidth(w);
35885     //        this.tree.innerCt.setHeight(h);
35886     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35887     //});
35888
35889         
35890     
35891 };
35892
35893 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35894     fitToFrame : true,
35895     autoScroll : true
35896 });
35897
35898
35899
35900
35901
35902
35903
35904
35905
35906
35907
35908 /*
35909  * Based on:
35910  * Ext JS Library 1.1.1
35911  * Copyright(c) 2006-2007, Ext JS, LLC.
35912  *
35913  * Originally Released Under LGPL - original licence link has changed is not relivant.
35914  *
35915  * Fork - LGPL
35916  * <script type="text/javascript">
35917  */
35918  
35919
35920 /**
35921  * @class Roo.ReaderLayout
35922  * @extends Roo.BorderLayout
35923  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35924  * center region containing two nested regions (a top one for a list view and one for item preview below),
35925  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35926  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35927  * expedites the setup of the overall layout and regions for this common application style.
35928  * Example:
35929  <pre><code>
35930 var reader = new Roo.ReaderLayout();
35931 var CP = Roo.ContentPanel;  // shortcut for adding
35932
35933 reader.beginUpdate();
35934 reader.add("north", new CP("north", "North"));
35935 reader.add("west", new CP("west", {title: "West"}));
35936 reader.add("east", new CP("east", {title: "East"}));
35937
35938 reader.regions.listView.add(new CP("listView", "List"));
35939 reader.regions.preview.add(new CP("preview", "Preview"));
35940 reader.endUpdate();
35941 </code></pre>
35942 * @constructor
35943 * Create a new ReaderLayout
35944 * @param {Object} config Configuration options
35945 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35946 * document.body if omitted)
35947 */
35948 Roo.ReaderLayout = function(config, renderTo){
35949     var c = config || {size:{}};
35950     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35951         north: c.north !== false ? Roo.apply({
35952             split:false,
35953             initialSize: 32,
35954             titlebar: false
35955         }, c.north) : false,
35956         west: c.west !== false ? Roo.apply({
35957             split:true,
35958             initialSize: 200,
35959             minSize: 175,
35960             maxSize: 400,
35961             titlebar: true,
35962             collapsible: true,
35963             animate: true,
35964             margins:{left:5,right:0,bottom:5,top:5},
35965             cmargins:{left:5,right:5,bottom:5,top:5}
35966         }, c.west) : false,
35967         east: c.east !== false ? Roo.apply({
35968             split:true,
35969             initialSize: 200,
35970             minSize: 175,
35971             maxSize: 400,
35972             titlebar: true,
35973             collapsible: true,
35974             animate: true,
35975             margins:{left:0,right:5,bottom:5,top:5},
35976             cmargins:{left:5,right:5,bottom:5,top:5}
35977         }, c.east) : false,
35978         center: Roo.apply({
35979             tabPosition: 'top',
35980             autoScroll:false,
35981             closeOnTab: true,
35982             titlebar:false,
35983             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35984         }, c.center)
35985     });
35986
35987     this.el.addClass('x-reader');
35988
35989     this.beginUpdate();
35990
35991     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35992         south: c.preview !== false ? Roo.apply({
35993             split:true,
35994             initialSize: 200,
35995             minSize: 100,
35996             autoScroll:true,
35997             collapsible:true,
35998             titlebar: true,
35999             cmargins:{top:5,left:0, right:0, bottom:0}
36000         }, c.preview) : false,
36001         center: Roo.apply({
36002             autoScroll:false,
36003             titlebar:false,
36004             minHeight:200
36005         }, c.listView)
36006     });
36007     this.add('center', new Roo.NestedLayoutPanel(inner,
36008             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36009
36010     this.endUpdate();
36011
36012     this.regions.preview = inner.getRegion('south');
36013     this.regions.listView = inner.getRegion('center');
36014 };
36015
36016 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36017  * Based on:
36018  * Ext JS Library 1.1.1
36019  * Copyright(c) 2006-2007, Ext JS, LLC.
36020  *
36021  * Originally Released Under LGPL - original licence link has changed is not relivant.
36022  *
36023  * Fork - LGPL
36024  * <script type="text/javascript">
36025  */
36026  
36027 /**
36028  * @class Roo.grid.Grid
36029  * @extends Roo.util.Observable
36030  * This class represents the primary interface of a component based grid control.
36031  * <br><br>Usage:<pre><code>
36032  var grid = new Roo.grid.Grid("my-container-id", {
36033      ds: myDataStore,
36034      cm: myColModel,
36035      selModel: mySelectionModel,
36036      autoSizeColumns: true,
36037      monitorWindowResize: false,
36038      trackMouseOver: true
36039  });
36040  // set any options
36041  grid.render();
36042  * </code></pre>
36043  * <b>Common Problems:</b><br/>
36044  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36045  * element will correct this<br/>
36046  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36047  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36048  * are unpredictable.<br/>
36049  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36050  * grid to calculate dimensions/offsets.<br/>
36051   * @constructor
36052  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36053  * The container MUST have some type of size defined for the grid to fill. The container will be
36054  * automatically set to position relative if it isn't already.
36055  * @param {Object} config A config object that sets properties on this grid.
36056  */
36057 Roo.grid.Grid = function(container, config){
36058         // initialize the container
36059         this.container = Roo.get(container);
36060         this.container.update("");
36061         this.container.setStyle("overflow", "hidden");
36062     this.container.addClass('x-grid-container');
36063
36064     this.id = this.container.id;
36065
36066     Roo.apply(this, config);
36067     // check and correct shorthanded configs
36068     if(this.ds){
36069         this.dataSource = this.ds;
36070         delete this.ds;
36071     }
36072     if(this.cm){
36073         this.colModel = this.cm;
36074         delete this.cm;
36075     }
36076     if(this.sm){
36077         this.selModel = this.sm;
36078         delete this.sm;
36079     }
36080
36081     if (this.selModel) {
36082         this.selModel = Roo.factory(this.selModel, Roo.grid);
36083         this.sm = this.selModel;
36084         this.sm.xmodule = this.xmodule || false;
36085     }
36086     if (typeof(this.colModel.config) == 'undefined') {
36087         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36088         this.cm = this.colModel;
36089         this.cm.xmodule = this.xmodule || false;
36090     }
36091     if (this.dataSource) {
36092         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36093         this.ds = this.dataSource;
36094         this.ds.xmodule = this.xmodule || false;
36095          
36096     }
36097     
36098     
36099     
36100     if(this.width){
36101         this.container.setWidth(this.width);
36102     }
36103
36104     if(this.height){
36105         this.container.setHeight(this.height);
36106     }
36107     /** @private */
36108         this.addEvents({
36109         // raw events
36110         /**
36111          * @event click
36112          * The raw click event for the entire grid.
36113          * @param {Roo.EventObject} e
36114          */
36115         "click" : true,
36116         /**
36117          * @event dblclick
36118          * The raw dblclick event for the entire grid.
36119          * @param {Roo.EventObject} e
36120          */
36121         "dblclick" : true,
36122         /**
36123          * @event contextmenu
36124          * The raw contextmenu event for the entire grid.
36125          * @param {Roo.EventObject} e
36126          */
36127         "contextmenu" : true,
36128         /**
36129          * @event mousedown
36130          * The raw mousedown event for the entire grid.
36131          * @param {Roo.EventObject} e
36132          */
36133         "mousedown" : true,
36134         /**
36135          * @event mouseup
36136          * The raw mouseup event for the entire grid.
36137          * @param {Roo.EventObject} e
36138          */
36139         "mouseup" : true,
36140         /**
36141          * @event mouseover
36142          * The raw mouseover event for the entire grid.
36143          * @param {Roo.EventObject} e
36144          */
36145         "mouseover" : true,
36146         /**
36147          * @event mouseout
36148          * The raw mouseout event for the entire grid.
36149          * @param {Roo.EventObject} e
36150          */
36151         "mouseout" : true,
36152         /**
36153          * @event keypress
36154          * The raw keypress event for the entire grid.
36155          * @param {Roo.EventObject} e
36156          */
36157         "keypress" : true,
36158         /**
36159          * @event keydown
36160          * The raw keydown event for the entire grid.
36161          * @param {Roo.EventObject} e
36162          */
36163         "keydown" : true,
36164
36165         // custom events
36166
36167         /**
36168          * @event cellclick
36169          * Fires when a cell is clicked
36170          * @param {Grid} this
36171          * @param {Number} rowIndex
36172          * @param {Number} columnIndex
36173          * @param {Roo.EventObject} e
36174          */
36175         "cellclick" : true,
36176         /**
36177          * @event celldblclick
36178          * Fires when a cell is double clicked
36179          * @param {Grid} this
36180          * @param {Number} rowIndex
36181          * @param {Number} columnIndex
36182          * @param {Roo.EventObject} e
36183          */
36184         "celldblclick" : true,
36185         /**
36186          * @event rowclick
36187          * Fires when a row is clicked
36188          * @param {Grid} this
36189          * @param {Number} rowIndex
36190          * @param {Roo.EventObject} e
36191          */
36192         "rowclick" : true,
36193         /**
36194          * @event rowdblclick
36195          * Fires when a row is double clicked
36196          * @param {Grid} this
36197          * @param {Number} rowIndex
36198          * @param {Roo.EventObject} e
36199          */
36200         "rowdblclick" : true,
36201         /**
36202          * @event headerclick
36203          * Fires when a header is clicked
36204          * @param {Grid} this
36205          * @param {Number} columnIndex
36206          * @param {Roo.EventObject} e
36207          */
36208         "headerclick" : true,
36209         /**
36210          * @event headerdblclick
36211          * Fires when a header cell is double clicked
36212          * @param {Grid} this
36213          * @param {Number} columnIndex
36214          * @param {Roo.EventObject} e
36215          */
36216         "headerdblclick" : true,
36217         /**
36218          * @event rowcontextmenu
36219          * Fires when a row is right clicked
36220          * @param {Grid} this
36221          * @param {Number} rowIndex
36222          * @param {Roo.EventObject} e
36223          */
36224         "rowcontextmenu" : true,
36225         /**
36226          * @event cellcontextmenu
36227          * Fires when a cell is right clicked
36228          * @param {Grid} this
36229          * @param {Number} rowIndex
36230          * @param {Number} cellIndex
36231          * @param {Roo.EventObject} e
36232          */
36233          "cellcontextmenu" : true,
36234         /**
36235          * @event headercontextmenu
36236          * Fires when a header is right clicked
36237          * @param {Grid} this
36238          * @param {Number} columnIndex
36239          * @param {Roo.EventObject} e
36240          */
36241         "headercontextmenu" : true,
36242         /**
36243          * @event bodyscroll
36244          * Fires when the body element is scrolled
36245          * @param {Number} scrollLeft
36246          * @param {Number} scrollTop
36247          */
36248         "bodyscroll" : true,
36249         /**
36250          * @event columnresize
36251          * Fires when the user resizes a column
36252          * @param {Number} columnIndex
36253          * @param {Number} newSize
36254          */
36255         "columnresize" : true,
36256         /**
36257          * @event columnmove
36258          * Fires when the user moves a column
36259          * @param {Number} oldIndex
36260          * @param {Number} newIndex
36261          */
36262         "columnmove" : true,
36263         /**
36264          * @event startdrag
36265          * Fires when row(s) start being dragged
36266          * @param {Grid} this
36267          * @param {Roo.GridDD} dd The drag drop object
36268          * @param {event} e The raw browser event
36269          */
36270         "startdrag" : true,
36271         /**
36272          * @event enddrag
36273          * Fires when a drag operation is complete
36274          * @param {Grid} this
36275          * @param {Roo.GridDD} dd The drag drop object
36276          * @param {event} e The raw browser event
36277          */
36278         "enddrag" : true,
36279         /**
36280          * @event dragdrop
36281          * Fires when dragged row(s) are dropped on a valid DD target
36282          * @param {Grid} this
36283          * @param {Roo.GridDD} dd The drag drop object
36284          * @param {String} targetId The target drag drop object
36285          * @param {event} e The raw browser event
36286          */
36287         "dragdrop" : true,
36288         /**
36289          * @event dragover
36290          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36291          * @param {Grid} this
36292          * @param {Roo.GridDD} dd The drag drop object
36293          * @param {String} targetId The target drag drop object
36294          * @param {event} e The raw browser event
36295          */
36296         "dragover" : true,
36297         /**
36298          * @event dragenter
36299          *  Fires when the dragged row(s) first cross another DD target while being dragged
36300          * @param {Grid} this
36301          * @param {Roo.GridDD} dd The drag drop object
36302          * @param {String} targetId The target drag drop object
36303          * @param {event} e The raw browser event
36304          */
36305         "dragenter" : true,
36306         /**
36307          * @event dragout
36308          * Fires when the dragged row(s) leave another DD target while being dragged
36309          * @param {Grid} this
36310          * @param {Roo.GridDD} dd The drag drop object
36311          * @param {String} targetId The target drag drop object
36312          * @param {event} e The raw browser event
36313          */
36314         "dragout" : true,
36315         /**
36316          * @event rowclass
36317          * Fires when a row is rendered, so you can change add a style to it.
36318          * @param {GridView} gridview   The grid view
36319          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36320          */
36321         'rowclass' : true,
36322
36323         /**
36324          * @event render
36325          * Fires when the grid is rendered
36326          * @param {Grid} grid
36327          */
36328         'render' : true
36329     });
36330
36331     Roo.grid.Grid.superclass.constructor.call(this);
36332 };
36333 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36334     
36335     /**
36336      * @cfg {String} ddGroup - drag drop group.
36337      */
36338
36339     /**
36340      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36341      */
36342     minColumnWidth : 25,
36343
36344     /**
36345      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36346      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36347      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36348      */
36349     autoSizeColumns : false,
36350
36351     /**
36352      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36353      */
36354     autoSizeHeaders : true,
36355
36356     /**
36357      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36358      */
36359     monitorWindowResize : true,
36360
36361     /**
36362      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36363      * rows measured to get a columns size. Default is 0 (all rows).
36364      */
36365     maxRowsToMeasure : 0,
36366
36367     /**
36368      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36369      */
36370     trackMouseOver : true,
36371
36372     /**
36373     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36374     */
36375     
36376     /**
36377     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36378     */
36379     enableDragDrop : false,
36380     
36381     /**
36382     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36383     */
36384     enableColumnMove : true,
36385     
36386     /**
36387     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36388     */
36389     enableColumnHide : true,
36390     
36391     /**
36392     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36393     */
36394     enableRowHeightSync : false,
36395     
36396     /**
36397     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36398     */
36399     stripeRows : true,
36400     
36401     /**
36402     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36403     */
36404     autoHeight : false,
36405
36406     /**
36407      * @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.
36408      */
36409     autoExpandColumn : false,
36410
36411     /**
36412     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36413     * Default is 50.
36414     */
36415     autoExpandMin : 50,
36416
36417     /**
36418     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36419     */
36420     autoExpandMax : 1000,
36421
36422     /**
36423     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36424     */
36425     view : null,
36426
36427     /**
36428     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36429     */
36430     loadMask : false,
36431     /**
36432     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36433     */
36434     dropTarget: false,
36435     
36436    
36437     
36438     // private
36439     rendered : false,
36440
36441     /**
36442     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36443     * of a fixed width. Default is false.
36444     */
36445     /**
36446     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36447     */
36448     /**
36449      * Called once after all setup has been completed and the grid is ready to be rendered.
36450      * @return {Roo.grid.Grid} this
36451      */
36452     render : function()
36453     {
36454         var c = this.container;
36455         // try to detect autoHeight/width mode
36456         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36457             this.autoHeight = true;
36458         }
36459         var view = this.getView();
36460         view.init(this);
36461
36462         c.on("click", this.onClick, this);
36463         c.on("dblclick", this.onDblClick, this);
36464         c.on("contextmenu", this.onContextMenu, this);
36465         c.on("keydown", this.onKeyDown, this);
36466         if (Roo.isTouch) {
36467             c.on("touchstart", this.onTouchStart, this);
36468         }
36469
36470         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36471
36472         this.getSelectionModel().init(this);
36473
36474         view.render();
36475
36476         if(this.loadMask){
36477             this.loadMask = new Roo.LoadMask(this.container,
36478                     Roo.apply({store:this.dataSource}, this.loadMask));
36479         }
36480         
36481         
36482         if (this.toolbar && this.toolbar.xtype) {
36483             this.toolbar.container = this.getView().getHeaderPanel(true);
36484             this.toolbar = new Roo.Toolbar(this.toolbar);
36485         }
36486         if (this.footer && this.footer.xtype) {
36487             this.footer.dataSource = this.getDataSource();
36488             this.footer.container = this.getView().getFooterPanel(true);
36489             this.footer = Roo.factory(this.footer, Roo);
36490         }
36491         if (this.dropTarget && this.dropTarget.xtype) {
36492             delete this.dropTarget.xtype;
36493             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36494         }
36495         
36496         
36497         this.rendered = true;
36498         this.fireEvent('render', this);
36499         return this;
36500     },
36501
36502         /**
36503          * Reconfigures the grid to use a different Store and Column Model.
36504          * The View will be bound to the new objects and refreshed.
36505          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36506          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36507          */
36508     reconfigure : function(dataSource, colModel){
36509         if(this.loadMask){
36510             this.loadMask.destroy();
36511             this.loadMask = new Roo.LoadMask(this.container,
36512                     Roo.apply({store:dataSource}, this.loadMask));
36513         }
36514         this.view.bind(dataSource, colModel);
36515         this.dataSource = dataSource;
36516         this.colModel = colModel;
36517         this.view.refresh(true);
36518     },
36519
36520     // private
36521     onKeyDown : function(e){
36522         this.fireEvent("keydown", e);
36523     },
36524
36525     /**
36526      * Destroy this grid.
36527      * @param {Boolean} removeEl True to remove the element
36528      */
36529     destroy : function(removeEl, keepListeners){
36530         if(this.loadMask){
36531             this.loadMask.destroy();
36532         }
36533         var c = this.container;
36534         c.removeAllListeners();
36535         this.view.destroy();
36536         this.colModel.purgeListeners();
36537         if(!keepListeners){
36538             this.purgeListeners();
36539         }
36540         c.update("");
36541         if(removeEl === true){
36542             c.remove();
36543         }
36544     },
36545
36546     // private
36547     processEvent : function(name, e){
36548         // does this fire select???
36549         //Roo.log('grid:processEvent '  + name);
36550         
36551         if (name != 'touchstart' ) {
36552             this.fireEvent(name, e);    
36553         }
36554         
36555         var t = e.getTarget();
36556         var v = this.view;
36557         var header = v.findHeaderIndex(t);
36558         if(header !== false){
36559             var ename = name == 'touchstart' ? 'click' : name;
36560              
36561             this.fireEvent("header" + ename, this, header, e);
36562         }else{
36563             var row = v.findRowIndex(t);
36564             var cell = v.findCellIndex(t);
36565             if (name == 'touchstart') {
36566                 // first touch is always a click.
36567                 // hopefull this happens after selection is updated.?
36568                 name = false;
36569                 
36570                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36571                     var cs = this.selModel.getSelectedCell();
36572                     if (row == cs[0] && cell == cs[1]){
36573                         name = 'dblclick';
36574                     }
36575                 }
36576                 if (typeof(this.selModel.getSelections) != 'undefined') {
36577                     var cs = this.selModel.getSelections();
36578                     var ds = this.dataSource;
36579                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36580                         name = 'dblclick';
36581                     }
36582                 }
36583                 if (!name) {
36584                     return;
36585                 }
36586             }
36587             
36588             
36589             if(row !== false){
36590                 this.fireEvent("row" + name, this, row, e);
36591                 if(cell !== false){
36592                     this.fireEvent("cell" + name, this, row, cell, e);
36593                 }
36594             }
36595         }
36596     },
36597
36598     // private
36599     onClick : function(e){
36600         this.processEvent("click", e);
36601     },
36602    // private
36603     onTouchStart : function(e){
36604         this.processEvent("touchstart", e);
36605     },
36606
36607     // private
36608     onContextMenu : function(e, t){
36609         this.processEvent("contextmenu", e);
36610     },
36611
36612     // private
36613     onDblClick : function(e){
36614         this.processEvent("dblclick", e);
36615     },
36616
36617     // private
36618     walkCells : function(row, col, step, fn, scope){
36619         var cm = this.colModel, clen = cm.getColumnCount();
36620         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36621         if(step < 0){
36622             if(col < 0){
36623                 row--;
36624                 first = false;
36625             }
36626             while(row >= 0){
36627                 if(!first){
36628                     col = clen-1;
36629                 }
36630                 first = false;
36631                 while(col >= 0){
36632                     if(fn.call(scope || this, row, col, cm) === true){
36633                         return [row, col];
36634                     }
36635                     col--;
36636                 }
36637                 row--;
36638             }
36639         } else {
36640             if(col >= clen){
36641                 row++;
36642                 first = false;
36643             }
36644             while(row < rlen){
36645                 if(!first){
36646                     col = 0;
36647                 }
36648                 first = false;
36649                 while(col < clen){
36650                     if(fn.call(scope || this, row, col, cm) === true){
36651                         return [row, col];
36652                     }
36653                     col++;
36654                 }
36655                 row++;
36656             }
36657         }
36658         return null;
36659     },
36660
36661     // private
36662     getSelections : function(){
36663         return this.selModel.getSelections();
36664     },
36665
36666     /**
36667      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36668      * but if manual update is required this method will initiate it.
36669      */
36670     autoSize : function(){
36671         if(this.rendered){
36672             this.view.layout();
36673             if(this.view.adjustForScroll){
36674                 this.view.adjustForScroll();
36675             }
36676         }
36677     },
36678
36679     /**
36680      * Returns the grid's underlying element.
36681      * @return {Element} The element
36682      */
36683     getGridEl : function(){
36684         return this.container;
36685     },
36686
36687     // private for compatibility, overridden by editor grid
36688     stopEditing : function(){},
36689
36690     /**
36691      * Returns the grid's SelectionModel.
36692      * @return {SelectionModel}
36693      */
36694     getSelectionModel : function(){
36695         if(!this.selModel){
36696             this.selModel = new Roo.grid.RowSelectionModel();
36697         }
36698         return this.selModel;
36699     },
36700
36701     /**
36702      * Returns the grid's DataSource.
36703      * @return {DataSource}
36704      */
36705     getDataSource : function(){
36706         return this.dataSource;
36707     },
36708
36709     /**
36710      * Returns the grid's ColumnModel.
36711      * @return {ColumnModel}
36712      */
36713     getColumnModel : function(){
36714         return this.colModel;
36715     },
36716
36717     /**
36718      * Returns the grid's GridView object.
36719      * @return {GridView}
36720      */
36721     getView : function(){
36722         if(!this.view){
36723             this.view = new Roo.grid.GridView(this.viewConfig);
36724         }
36725         return this.view;
36726     },
36727     /**
36728      * Called to get grid's drag proxy text, by default returns this.ddText.
36729      * @return {String}
36730      */
36731     getDragDropText : function(){
36732         var count = this.selModel.getCount();
36733         return String.format(this.ddText, count, count == 1 ? '' : 's');
36734     }
36735 });
36736 /**
36737  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36738  * %0 is replaced with the number of selected rows.
36739  * @type String
36740  */
36741 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36742  * Based on:
36743  * Ext JS Library 1.1.1
36744  * Copyright(c) 2006-2007, Ext JS, LLC.
36745  *
36746  * Originally Released Under LGPL - original licence link has changed is not relivant.
36747  *
36748  * Fork - LGPL
36749  * <script type="text/javascript">
36750  */
36751  
36752 Roo.grid.AbstractGridView = function(){
36753         this.grid = null;
36754         
36755         this.events = {
36756             "beforerowremoved" : true,
36757             "beforerowsinserted" : true,
36758             "beforerefresh" : true,
36759             "rowremoved" : true,
36760             "rowsinserted" : true,
36761             "rowupdated" : true,
36762             "refresh" : true
36763         };
36764     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36765 };
36766
36767 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36768     rowClass : "x-grid-row",
36769     cellClass : "x-grid-cell",
36770     tdClass : "x-grid-td",
36771     hdClass : "x-grid-hd",
36772     splitClass : "x-grid-hd-split",
36773     
36774     init: function(grid){
36775         this.grid = grid;
36776                 var cid = this.grid.getGridEl().id;
36777         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36778         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36779         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36780         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36781         },
36782         
36783     getColumnRenderers : function(){
36784         var renderers = [];
36785         var cm = this.grid.colModel;
36786         var colCount = cm.getColumnCount();
36787         for(var i = 0; i < colCount; i++){
36788             renderers[i] = cm.getRenderer(i);
36789         }
36790         return renderers;
36791     },
36792     
36793     getColumnIds : function(){
36794         var ids = [];
36795         var cm = this.grid.colModel;
36796         var colCount = cm.getColumnCount();
36797         for(var i = 0; i < colCount; i++){
36798             ids[i] = cm.getColumnId(i);
36799         }
36800         return ids;
36801     },
36802     
36803     getDataIndexes : function(){
36804         if(!this.indexMap){
36805             this.indexMap = this.buildIndexMap();
36806         }
36807         return this.indexMap.colToData;
36808     },
36809     
36810     getColumnIndexByDataIndex : function(dataIndex){
36811         if(!this.indexMap){
36812             this.indexMap = this.buildIndexMap();
36813         }
36814         return this.indexMap.dataToCol[dataIndex];
36815     },
36816     
36817     /**
36818      * Set a css style for a column dynamically. 
36819      * @param {Number} colIndex The index of the column
36820      * @param {String} name The css property name
36821      * @param {String} value The css value
36822      */
36823     setCSSStyle : function(colIndex, name, value){
36824         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36825         Roo.util.CSS.updateRule(selector, name, value);
36826     },
36827     
36828     generateRules : function(cm){
36829         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36830         Roo.util.CSS.removeStyleSheet(rulesId);
36831         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36832             var cid = cm.getColumnId(i);
36833             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36834                          this.tdSelector, cid, " {\n}\n",
36835                          this.hdSelector, cid, " {\n}\n",
36836                          this.splitSelector, cid, " {\n}\n");
36837         }
36838         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36839     }
36840 });/*
36841  * Based on:
36842  * Ext JS Library 1.1.1
36843  * Copyright(c) 2006-2007, Ext JS, LLC.
36844  *
36845  * Originally Released Under LGPL - original licence link has changed is not relivant.
36846  *
36847  * Fork - LGPL
36848  * <script type="text/javascript">
36849  */
36850
36851 // private
36852 // This is a support class used internally by the Grid components
36853 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36854     this.grid = grid;
36855     this.view = grid.getView();
36856     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36857     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36858     if(hd2){
36859         this.setHandleElId(Roo.id(hd));
36860         this.setOuterHandleElId(Roo.id(hd2));
36861     }
36862     this.scroll = false;
36863 };
36864 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36865     maxDragWidth: 120,
36866     getDragData : function(e){
36867         var t = Roo.lib.Event.getTarget(e);
36868         var h = this.view.findHeaderCell(t);
36869         if(h){
36870             return {ddel: h.firstChild, header:h};
36871         }
36872         return false;
36873     },
36874
36875     onInitDrag : function(e){
36876         this.view.headersDisabled = true;
36877         var clone = this.dragData.ddel.cloneNode(true);
36878         clone.id = Roo.id();
36879         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36880         this.proxy.update(clone);
36881         return true;
36882     },
36883
36884     afterValidDrop : function(){
36885         var v = this.view;
36886         setTimeout(function(){
36887             v.headersDisabled = false;
36888         }, 50);
36889     },
36890
36891     afterInvalidDrop : function(){
36892         var v = this.view;
36893         setTimeout(function(){
36894             v.headersDisabled = false;
36895         }, 50);
36896     }
36897 });
36898 /*
36899  * Based on:
36900  * Ext JS Library 1.1.1
36901  * Copyright(c) 2006-2007, Ext JS, LLC.
36902  *
36903  * Originally Released Under LGPL - original licence link has changed is not relivant.
36904  *
36905  * Fork - LGPL
36906  * <script type="text/javascript">
36907  */
36908 // private
36909 // This is a support class used internally by the Grid components
36910 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36911     this.grid = grid;
36912     this.view = grid.getView();
36913     // split the proxies so they don't interfere with mouse events
36914     this.proxyTop = Roo.DomHelper.append(document.body, {
36915         cls:"col-move-top", html:"&#160;"
36916     }, true);
36917     this.proxyBottom = Roo.DomHelper.append(document.body, {
36918         cls:"col-move-bottom", html:"&#160;"
36919     }, true);
36920     this.proxyTop.hide = this.proxyBottom.hide = function(){
36921         this.setLeftTop(-100,-100);
36922         this.setStyle("visibility", "hidden");
36923     };
36924     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36925     // temporarily disabled
36926     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36927     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36928 };
36929 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36930     proxyOffsets : [-4, -9],
36931     fly: Roo.Element.fly,
36932
36933     getTargetFromEvent : function(e){
36934         var t = Roo.lib.Event.getTarget(e);
36935         var cindex = this.view.findCellIndex(t);
36936         if(cindex !== false){
36937             return this.view.getHeaderCell(cindex);
36938         }
36939         return null;
36940     },
36941
36942     nextVisible : function(h){
36943         var v = this.view, cm = this.grid.colModel;
36944         h = h.nextSibling;
36945         while(h){
36946             if(!cm.isHidden(v.getCellIndex(h))){
36947                 return h;
36948             }
36949             h = h.nextSibling;
36950         }
36951         return null;
36952     },
36953
36954     prevVisible : function(h){
36955         var v = this.view, cm = this.grid.colModel;
36956         h = h.prevSibling;
36957         while(h){
36958             if(!cm.isHidden(v.getCellIndex(h))){
36959                 return h;
36960             }
36961             h = h.prevSibling;
36962         }
36963         return null;
36964     },
36965
36966     positionIndicator : function(h, n, e){
36967         var x = Roo.lib.Event.getPageX(e);
36968         var r = Roo.lib.Dom.getRegion(n.firstChild);
36969         var px, pt, py = r.top + this.proxyOffsets[1];
36970         if((r.right - x) <= (r.right-r.left)/2){
36971             px = r.right+this.view.borderWidth;
36972             pt = "after";
36973         }else{
36974             px = r.left;
36975             pt = "before";
36976         }
36977         var oldIndex = this.view.getCellIndex(h);
36978         var newIndex = this.view.getCellIndex(n);
36979
36980         if(this.grid.colModel.isFixed(newIndex)){
36981             return false;
36982         }
36983
36984         var locked = this.grid.colModel.isLocked(newIndex);
36985
36986         if(pt == "after"){
36987             newIndex++;
36988         }
36989         if(oldIndex < newIndex){
36990             newIndex--;
36991         }
36992         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36993             return false;
36994         }
36995         px +=  this.proxyOffsets[0];
36996         this.proxyTop.setLeftTop(px, py);
36997         this.proxyTop.show();
36998         if(!this.bottomOffset){
36999             this.bottomOffset = this.view.mainHd.getHeight();
37000         }
37001         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37002         this.proxyBottom.show();
37003         return pt;
37004     },
37005
37006     onNodeEnter : function(n, dd, e, data){
37007         if(data.header != n){
37008             this.positionIndicator(data.header, n, e);
37009         }
37010     },
37011
37012     onNodeOver : function(n, dd, e, data){
37013         var result = false;
37014         if(data.header != n){
37015             result = this.positionIndicator(data.header, n, e);
37016         }
37017         if(!result){
37018             this.proxyTop.hide();
37019             this.proxyBottom.hide();
37020         }
37021         return result ? this.dropAllowed : this.dropNotAllowed;
37022     },
37023
37024     onNodeOut : function(n, dd, e, data){
37025         this.proxyTop.hide();
37026         this.proxyBottom.hide();
37027     },
37028
37029     onNodeDrop : function(n, dd, e, data){
37030         var h = data.header;
37031         if(h != n){
37032             var cm = this.grid.colModel;
37033             var x = Roo.lib.Event.getPageX(e);
37034             var r = Roo.lib.Dom.getRegion(n.firstChild);
37035             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37036             var oldIndex = this.view.getCellIndex(h);
37037             var newIndex = this.view.getCellIndex(n);
37038             var locked = cm.isLocked(newIndex);
37039             if(pt == "after"){
37040                 newIndex++;
37041             }
37042             if(oldIndex < newIndex){
37043                 newIndex--;
37044             }
37045             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37046                 return false;
37047             }
37048             cm.setLocked(oldIndex, locked, true);
37049             cm.moveColumn(oldIndex, newIndex);
37050             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37051             return true;
37052         }
37053         return false;
37054     }
37055 });
37056 /*
37057  * Based on:
37058  * Ext JS Library 1.1.1
37059  * Copyright(c) 2006-2007, Ext JS, LLC.
37060  *
37061  * Originally Released Under LGPL - original licence link has changed is not relivant.
37062  *
37063  * Fork - LGPL
37064  * <script type="text/javascript">
37065  */
37066   
37067 /**
37068  * @class Roo.grid.GridView
37069  * @extends Roo.util.Observable
37070  *
37071  * @constructor
37072  * @param {Object} config
37073  */
37074 Roo.grid.GridView = function(config){
37075     Roo.grid.GridView.superclass.constructor.call(this);
37076     this.el = null;
37077
37078     Roo.apply(this, config);
37079 };
37080
37081 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37082
37083     unselectable :  'unselectable="on"',
37084     unselectableCls :  'x-unselectable',
37085     
37086     
37087     rowClass : "x-grid-row",
37088
37089     cellClass : "x-grid-col",
37090
37091     tdClass : "x-grid-td",
37092
37093     hdClass : "x-grid-hd",
37094
37095     splitClass : "x-grid-split",
37096
37097     sortClasses : ["sort-asc", "sort-desc"],
37098
37099     enableMoveAnim : false,
37100
37101     hlColor: "C3DAF9",
37102
37103     dh : Roo.DomHelper,
37104
37105     fly : Roo.Element.fly,
37106
37107     css : Roo.util.CSS,
37108
37109     borderWidth: 1,
37110
37111     splitOffset: 3,
37112
37113     scrollIncrement : 22,
37114
37115     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37116
37117     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37118
37119     bind : function(ds, cm){
37120         if(this.ds){
37121             this.ds.un("load", this.onLoad, this);
37122             this.ds.un("datachanged", this.onDataChange, this);
37123             this.ds.un("add", this.onAdd, this);
37124             this.ds.un("remove", this.onRemove, this);
37125             this.ds.un("update", this.onUpdate, this);
37126             this.ds.un("clear", this.onClear, this);
37127         }
37128         if(ds){
37129             ds.on("load", this.onLoad, this);
37130             ds.on("datachanged", this.onDataChange, this);
37131             ds.on("add", this.onAdd, this);
37132             ds.on("remove", this.onRemove, this);
37133             ds.on("update", this.onUpdate, this);
37134             ds.on("clear", this.onClear, this);
37135         }
37136         this.ds = ds;
37137
37138         if(this.cm){
37139             this.cm.un("widthchange", this.onColWidthChange, this);
37140             this.cm.un("headerchange", this.onHeaderChange, this);
37141             this.cm.un("hiddenchange", this.onHiddenChange, this);
37142             this.cm.un("columnmoved", this.onColumnMove, this);
37143             this.cm.un("columnlockchange", this.onColumnLock, this);
37144         }
37145         if(cm){
37146             this.generateRules(cm);
37147             cm.on("widthchange", this.onColWidthChange, this);
37148             cm.on("headerchange", this.onHeaderChange, this);
37149             cm.on("hiddenchange", this.onHiddenChange, this);
37150             cm.on("columnmoved", this.onColumnMove, this);
37151             cm.on("columnlockchange", this.onColumnLock, this);
37152         }
37153         this.cm = cm;
37154     },
37155
37156     init: function(grid){
37157         Roo.grid.GridView.superclass.init.call(this, grid);
37158
37159         this.bind(grid.dataSource, grid.colModel);
37160
37161         grid.on("headerclick", this.handleHeaderClick, this);
37162
37163         if(grid.trackMouseOver){
37164             grid.on("mouseover", this.onRowOver, this);
37165             grid.on("mouseout", this.onRowOut, this);
37166         }
37167         grid.cancelTextSelection = function(){};
37168         this.gridId = grid.id;
37169
37170         var tpls = this.templates || {};
37171
37172         if(!tpls.master){
37173             tpls.master = new Roo.Template(
37174                '<div class="x-grid" hidefocus="true">',
37175                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37176                   '<div class="x-grid-topbar"></div>',
37177                   '<div class="x-grid-scroller"><div></div></div>',
37178                   '<div class="x-grid-locked">',
37179                       '<div class="x-grid-header">{lockedHeader}</div>',
37180                       '<div class="x-grid-body">{lockedBody}</div>',
37181                   "</div>",
37182                   '<div class="x-grid-viewport">',
37183                       '<div class="x-grid-header">{header}</div>',
37184                       '<div class="x-grid-body">{body}</div>',
37185                   "</div>",
37186                   '<div class="x-grid-bottombar"></div>',
37187                  
37188                   '<div class="x-grid-resize-proxy">&#160;</div>',
37189                "</div>"
37190             );
37191             tpls.master.disableformats = true;
37192         }
37193
37194         if(!tpls.header){
37195             tpls.header = new Roo.Template(
37196                '<table border="0" cellspacing="0" cellpadding="0">',
37197                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37198                "</table>{splits}"
37199             );
37200             tpls.header.disableformats = true;
37201         }
37202         tpls.header.compile();
37203
37204         if(!tpls.hcell){
37205             tpls.hcell = new Roo.Template(
37206                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37207                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37208                 "</div></td>"
37209              );
37210              tpls.hcell.disableFormats = true;
37211         }
37212         tpls.hcell.compile();
37213
37214         if(!tpls.hsplit){
37215             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37216                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37217             tpls.hsplit.disableFormats = true;
37218         }
37219         tpls.hsplit.compile();
37220
37221         if(!tpls.body){
37222             tpls.body = new Roo.Template(
37223                '<table border="0" cellspacing="0" cellpadding="0">',
37224                "<tbody>{rows}</tbody>",
37225                "</table>"
37226             );
37227             tpls.body.disableFormats = true;
37228         }
37229         tpls.body.compile();
37230
37231         if(!tpls.row){
37232             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37233             tpls.row.disableFormats = true;
37234         }
37235         tpls.row.compile();
37236
37237         if(!tpls.cell){
37238             tpls.cell = new Roo.Template(
37239                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37240                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37241                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37242                 "</td>"
37243             );
37244             tpls.cell.disableFormats = true;
37245         }
37246         tpls.cell.compile();
37247
37248         this.templates = tpls;
37249     },
37250
37251     // remap these for backwards compat
37252     onColWidthChange : function(){
37253         this.updateColumns.apply(this, arguments);
37254     },
37255     onHeaderChange : function(){
37256         this.updateHeaders.apply(this, arguments);
37257     }, 
37258     onHiddenChange : function(){
37259         this.handleHiddenChange.apply(this, arguments);
37260     },
37261     onColumnMove : function(){
37262         this.handleColumnMove.apply(this, arguments);
37263     },
37264     onColumnLock : function(){
37265         this.handleLockChange.apply(this, arguments);
37266     },
37267
37268     onDataChange : function(){
37269         this.refresh();
37270         this.updateHeaderSortState();
37271     },
37272
37273     onClear : function(){
37274         this.refresh();
37275     },
37276
37277     onUpdate : function(ds, record){
37278         this.refreshRow(record);
37279     },
37280
37281     refreshRow : function(record){
37282         var ds = this.ds, index;
37283         if(typeof record == 'number'){
37284             index = record;
37285             record = ds.getAt(index);
37286         }else{
37287             index = ds.indexOf(record);
37288         }
37289         this.insertRows(ds, index, index, true);
37290         this.onRemove(ds, record, index+1, true);
37291         this.syncRowHeights(index, index);
37292         this.layout();
37293         this.fireEvent("rowupdated", this, index, record);
37294     },
37295
37296     onAdd : function(ds, records, index){
37297         this.insertRows(ds, index, index + (records.length-1));
37298     },
37299
37300     onRemove : function(ds, record, index, isUpdate){
37301         if(isUpdate !== true){
37302             this.fireEvent("beforerowremoved", this, index, record);
37303         }
37304         var bt = this.getBodyTable(), lt = this.getLockedTable();
37305         if(bt.rows[index]){
37306             bt.firstChild.removeChild(bt.rows[index]);
37307         }
37308         if(lt.rows[index]){
37309             lt.firstChild.removeChild(lt.rows[index]);
37310         }
37311         if(isUpdate !== true){
37312             this.stripeRows(index);
37313             this.syncRowHeights(index, index);
37314             this.layout();
37315             this.fireEvent("rowremoved", this, index, record);
37316         }
37317     },
37318
37319     onLoad : function(){
37320         this.scrollToTop();
37321     },
37322
37323     /**
37324      * Scrolls the grid to the top
37325      */
37326     scrollToTop : function(){
37327         if(this.scroller){
37328             this.scroller.dom.scrollTop = 0;
37329             this.syncScroll();
37330         }
37331     },
37332
37333     /**
37334      * Gets a panel in the header of the grid that can be used for toolbars etc.
37335      * After modifying the contents of this panel a call to grid.autoSize() may be
37336      * required to register any changes in size.
37337      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37338      * @return Roo.Element
37339      */
37340     getHeaderPanel : function(doShow){
37341         if(doShow){
37342             this.headerPanel.show();
37343         }
37344         return this.headerPanel;
37345     },
37346
37347     /**
37348      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37349      * After modifying the contents of this panel a call to grid.autoSize() may be
37350      * required to register any changes in size.
37351      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37352      * @return Roo.Element
37353      */
37354     getFooterPanel : function(doShow){
37355         if(doShow){
37356             this.footerPanel.show();
37357         }
37358         return this.footerPanel;
37359     },
37360
37361     initElements : function(){
37362         var E = Roo.Element;
37363         var el = this.grid.getGridEl().dom.firstChild;
37364         var cs = el.childNodes;
37365
37366         this.el = new E(el);
37367         
37368          this.focusEl = new E(el.firstChild);
37369         this.focusEl.swallowEvent("click", true);
37370         
37371         this.headerPanel = new E(cs[1]);
37372         this.headerPanel.enableDisplayMode("block");
37373
37374         this.scroller = new E(cs[2]);
37375         this.scrollSizer = new E(this.scroller.dom.firstChild);
37376
37377         this.lockedWrap = new E(cs[3]);
37378         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37379         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37380
37381         this.mainWrap = new E(cs[4]);
37382         this.mainHd = new E(this.mainWrap.dom.firstChild);
37383         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37384
37385         this.footerPanel = new E(cs[5]);
37386         this.footerPanel.enableDisplayMode("block");
37387
37388         this.resizeProxy = new E(cs[6]);
37389
37390         this.headerSelector = String.format(
37391            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37392            this.lockedHd.id, this.mainHd.id
37393         );
37394
37395         this.splitterSelector = String.format(
37396            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37397            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37398         );
37399     },
37400     idToCssName : function(s)
37401     {
37402         return s.replace(/[^a-z0-9]+/ig, '-');
37403     },
37404
37405     getHeaderCell : function(index){
37406         return Roo.DomQuery.select(this.headerSelector)[index];
37407     },
37408
37409     getHeaderCellMeasure : function(index){
37410         return this.getHeaderCell(index).firstChild;
37411     },
37412
37413     getHeaderCellText : function(index){
37414         return this.getHeaderCell(index).firstChild.firstChild;
37415     },
37416
37417     getLockedTable : function(){
37418         return this.lockedBody.dom.firstChild;
37419     },
37420
37421     getBodyTable : function(){
37422         return this.mainBody.dom.firstChild;
37423     },
37424
37425     getLockedRow : function(index){
37426         return this.getLockedTable().rows[index];
37427     },
37428
37429     getRow : function(index){
37430         return this.getBodyTable().rows[index];
37431     },
37432
37433     getRowComposite : function(index){
37434         if(!this.rowEl){
37435             this.rowEl = new Roo.CompositeElementLite();
37436         }
37437         var els = [], lrow, mrow;
37438         if(lrow = this.getLockedRow(index)){
37439             els.push(lrow);
37440         }
37441         if(mrow = this.getRow(index)){
37442             els.push(mrow);
37443         }
37444         this.rowEl.elements = els;
37445         return this.rowEl;
37446     },
37447     /**
37448      * Gets the 'td' of the cell
37449      * 
37450      * @param {Integer} rowIndex row to select
37451      * @param {Integer} colIndex column to select
37452      * 
37453      * @return {Object} 
37454      */
37455     getCell : function(rowIndex, colIndex){
37456         var locked = this.cm.getLockedCount();
37457         var source;
37458         if(colIndex < locked){
37459             source = this.lockedBody.dom.firstChild;
37460         }else{
37461             source = this.mainBody.dom.firstChild;
37462             colIndex -= locked;
37463         }
37464         return source.rows[rowIndex].childNodes[colIndex];
37465     },
37466
37467     getCellText : function(rowIndex, colIndex){
37468         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37469     },
37470
37471     getCellBox : function(cell){
37472         var b = this.fly(cell).getBox();
37473         if(Roo.isOpera){ // opera fails to report the Y
37474             b.y = cell.offsetTop + this.mainBody.getY();
37475         }
37476         return b;
37477     },
37478
37479     getCellIndex : function(cell){
37480         var id = String(cell.className).match(this.cellRE);
37481         if(id){
37482             return parseInt(id[1], 10);
37483         }
37484         return 0;
37485     },
37486
37487     findHeaderIndex : function(n){
37488         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37489         return r ? this.getCellIndex(r) : false;
37490     },
37491
37492     findHeaderCell : function(n){
37493         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37494         return r ? r : false;
37495     },
37496
37497     findRowIndex : function(n){
37498         if(!n){
37499             return false;
37500         }
37501         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37502         return r ? r.rowIndex : false;
37503     },
37504
37505     findCellIndex : function(node){
37506         var stop = this.el.dom;
37507         while(node && node != stop){
37508             if(this.findRE.test(node.className)){
37509                 return this.getCellIndex(node);
37510             }
37511             node = node.parentNode;
37512         }
37513         return false;
37514     },
37515
37516     getColumnId : function(index){
37517         return this.cm.getColumnId(index);
37518     },
37519
37520     getSplitters : function()
37521     {
37522         if(this.splitterSelector){
37523            return Roo.DomQuery.select(this.splitterSelector);
37524         }else{
37525             return null;
37526       }
37527     },
37528
37529     getSplitter : function(index){
37530         return this.getSplitters()[index];
37531     },
37532
37533     onRowOver : function(e, t){
37534         var row;
37535         if((row = this.findRowIndex(t)) !== false){
37536             this.getRowComposite(row).addClass("x-grid-row-over");
37537         }
37538     },
37539
37540     onRowOut : function(e, t){
37541         var row;
37542         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37543             this.getRowComposite(row).removeClass("x-grid-row-over");
37544         }
37545     },
37546
37547     renderHeaders : function(){
37548         var cm = this.cm;
37549         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37550         var cb = [], lb = [], sb = [], lsb = [], p = {};
37551         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37552             p.cellId = "x-grid-hd-0-" + i;
37553             p.splitId = "x-grid-csplit-0-" + i;
37554             p.id = cm.getColumnId(i);
37555             p.value = cm.getColumnHeader(i) || "";
37556             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
37557             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37558             if(!cm.isLocked(i)){
37559                 cb[cb.length] = ct.apply(p);
37560                 sb[sb.length] = st.apply(p);
37561             }else{
37562                 lb[lb.length] = ct.apply(p);
37563                 lsb[lsb.length] = st.apply(p);
37564             }
37565         }
37566         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37567                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37568     },
37569
37570     updateHeaders : function(){
37571         var html = this.renderHeaders();
37572         this.lockedHd.update(html[0]);
37573         this.mainHd.update(html[1]);
37574     },
37575
37576     /**
37577      * Focuses the specified row.
37578      * @param {Number} row The row index
37579      */
37580     focusRow : function(row)
37581     {
37582         //Roo.log('GridView.focusRow');
37583         var x = this.scroller.dom.scrollLeft;
37584         this.focusCell(row, 0, false);
37585         this.scroller.dom.scrollLeft = x;
37586     },
37587
37588     /**
37589      * Focuses the specified cell.
37590      * @param {Number} row The row index
37591      * @param {Number} col The column index
37592      * @param {Boolean} hscroll false to disable horizontal scrolling
37593      */
37594     focusCell : function(row, col, hscroll)
37595     {
37596         //Roo.log('GridView.focusCell');
37597         var el = this.ensureVisible(row, col, hscroll);
37598         this.focusEl.alignTo(el, "tl-tl");
37599         if(Roo.isGecko){
37600             this.focusEl.focus();
37601         }else{
37602             this.focusEl.focus.defer(1, this.focusEl);
37603         }
37604     },
37605
37606     /**
37607      * Scrolls the specified cell into view
37608      * @param {Number} row The row index
37609      * @param {Number} col The column index
37610      * @param {Boolean} hscroll false to disable horizontal scrolling
37611      */
37612     ensureVisible : function(row, col, hscroll)
37613     {
37614         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37615         //return null; //disable for testing.
37616         if(typeof row != "number"){
37617             row = row.rowIndex;
37618         }
37619         if(row < 0 && row >= this.ds.getCount()){
37620             return  null;
37621         }
37622         col = (col !== undefined ? col : 0);
37623         var cm = this.grid.colModel;
37624         while(cm.isHidden(col)){
37625             col++;
37626         }
37627
37628         var el = this.getCell(row, col);
37629         if(!el){
37630             return null;
37631         }
37632         var c = this.scroller.dom;
37633
37634         var ctop = parseInt(el.offsetTop, 10);
37635         var cleft = parseInt(el.offsetLeft, 10);
37636         var cbot = ctop + el.offsetHeight;
37637         var cright = cleft + el.offsetWidth;
37638         
37639         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37640         var stop = parseInt(c.scrollTop, 10);
37641         var sleft = parseInt(c.scrollLeft, 10);
37642         var sbot = stop + ch;
37643         var sright = sleft + c.clientWidth;
37644         /*
37645         Roo.log('GridView.ensureVisible:' +
37646                 ' ctop:' + ctop +
37647                 ' c.clientHeight:' + c.clientHeight +
37648                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37649                 ' stop:' + stop +
37650                 ' cbot:' + cbot +
37651                 ' sbot:' + sbot +
37652                 ' ch:' + ch  
37653                 );
37654         */
37655         if(ctop < stop){
37656              c.scrollTop = ctop;
37657             //Roo.log("set scrolltop to ctop DISABLE?");
37658         }else if(cbot > sbot){
37659             //Roo.log("set scrolltop to cbot-ch");
37660             c.scrollTop = cbot-ch;
37661         }
37662         
37663         if(hscroll !== false){
37664             if(cleft < sleft){
37665                 c.scrollLeft = cleft;
37666             }else if(cright > sright){
37667                 c.scrollLeft = cright-c.clientWidth;
37668             }
37669         }
37670          
37671         return el;
37672     },
37673
37674     updateColumns : function(){
37675         this.grid.stopEditing();
37676         var cm = this.grid.colModel, colIds = this.getColumnIds();
37677         //var totalWidth = cm.getTotalWidth();
37678         var pos = 0;
37679         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37680             //if(cm.isHidden(i)) continue;
37681             var w = cm.getColumnWidth(i);
37682             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37683             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37684         }
37685         this.updateSplitters();
37686     },
37687
37688     generateRules : function(cm){
37689         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37690         Roo.util.CSS.removeStyleSheet(rulesId);
37691         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37692             var cid = cm.getColumnId(i);
37693             var align = '';
37694             if(cm.config[i].align){
37695                 align = 'text-align:'+cm.config[i].align+';';
37696             }
37697             var hidden = '';
37698             if(cm.isHidden(i)){
37699                 hidden = 'display:none;';
37700             }
37701             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37702             ruleBuf.push(
37703                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37704                     this.hdSelector, cid, " {\n", align, width, "}\n",
37705                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37706                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37707         }
37708         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37709     },
37710
37711     updateSplitters : function(){
37712         var cm = this.cm, s = this.getSplitters();
37713         if(s){ // splitters not created yet
37714             var pos = 0, locked = true;
37715             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37716                 if(cm.isHidden(i)) {
37717                     continue;
37718                 }
37719                 var w = cm.getColumnWidth(i); // make sure it's a number
37720                 if(!cm.isLocked(i) && locked){
37721                     pos = 0;
37722                     locked = false;
37723                 }
37724                 pos += w;
37725                 s[i].style.left = (pos-this.splitOffset) + "px";
37726             }
37727         }
37728     },
37729
37730     handleHiddenChange : function(colModel, colIndex, hidden){
37731         if(hidden){
37732             this.hideColumn(colIndex);
37733         }else{
37734             this.unhideColumn(colIndex);
37735         }
37736     },
37737
37738     hideColumn : function(colIndex){
37739         var cid = this.getColumnId(colIndex);
37740         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37741         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37742         if(Roo.isSafari){
37743             this.updateHeaders();
37744         }
37745         this.updateSplitters();
37746         this.layout();
37747     },
37748
37749     unhideColumn : function(colIndex){
37750         var cid = this.getColumnId(colIndex);
37751         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37752         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37753
37754         if(Roo.isSafari){
37755             this.updateHeaders();
37756         }
37757         this.updateSplitters();
37758         this.layout();
37759     },
37760
37761     insertRows : function(dm, firstRow, lastRow, isUpdate){
37762         if(firstRow == 0 && lastRow == dm.getCount()-1){
37763             this.refresh();
37764         }else{
37765             if(!isUpdate){
37766                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37767             }
37768             var s = this.getScrollState();
37769             var markup = this.renderRows(firstRow, lastRow);
37770             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37771             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37772             this.restoreScroll(s);
37773             if(!isUpdate){
37774                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37775                 this.syncRowHeights(firstRow, lastRow);
37776                 this.stripeRows(firstRow);
37777                 this.layout();
37778             }
37779         }
37780     },
37781
37782     bufferRows : function(markup, target, index){
37783         var before = null, trows = target.rows, tbody = target.tBodies[0];
37784         if(index < trows.length){
37785             before = trows[index];
37786         }
37787         var b = document.createElement("div");
37788         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37789         var rows = b.firstChild.rows;
37790         for(var i = 0, len = rows.length; i < len; i++){
37791             if(before){
37792                 tbody.insertBefore(rows[0], before);
37793             }else{
37794                 tbody.appendChild(rows[0]);
37795             }
37796         }
37797         b.innerHTML = "";
37798         b = null;
37799     },
37800
37801     deleteRows : function(dm, firstRow, lastRow){
37802         if(dm.getRowCount()<1){
37803             this.fireEvent("beforerefresh", this);
37804             this.mainBody.update("");
37805             this.lockedBody.update("");
37806             this.fireEvent("refresh", this);
37807         }else{
37808             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37809             var bt = this.getBodyTable();
37810             var tbody = bt.firstChild;
37811             var rows = bt.rows;
37812             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37813                 tbody.removeChild(rows[firstRow]);
37814             }
37815             this.stripeRows(firstRow);
37816             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37817         }
37818     },
37819
37820     updateRows : function(dataSource, firstRow, lastRow){
37821         var s = this.getScrollState();
37822         this.refresh();
37823         this.restoreScroll(s);
37824     },
37825
37826     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37827         if(!noRefresh){
37828            this.refresh();
37829         }
37830         this.updateHeaderSortState();
37831     },
37832
37833     getScrollState : function(){
37834         
37835         var sb = this.scroller.dom;
37836         return {left: sb.scrollLeft, top: sb.scrollTop};
37837     },
37838
37839     stripeRows : function(startRow){
37840         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37841             return;
37842         }
37843         startRow = startRow || 0;
37844         var rows = this.getBodyTable().rows;
37845         var lrows = this.getLockedTable().rows;
37846         var cls = ' x-grid-row-alt ';
37847         for(var i = startRow, len = rows.length; i < len; i++){
37848             var row = rows[i], lrow = lrows[i];
37849             var isAlt = ((i+1) % 2 == 0);
37850             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37851             if(isAlt == hasAlt){
37852                 continue;
37853             }
37854             if(isAlt){
37855                 row.className += " x-grid-row-alt";
37856             }else{
37857                 row.className = row.className.replace("x-grid-row-alt", "");
37858             }
37859             if(lrow){
37860                 lrow.className = row.className;
37861             }
37862         }
37863     },
37864
37865     restoreScroll : function(state){
37866         //Roo.log('GridView.restoreScroll');
37867         var sb = this.scroller.dom;
37868         sb.scrollLeft = state.left;
37869         sb.scrollTop = state.top;
37870         this.syncScroll();
37871     },
37872
37873     syncScroll : function(){
37874         //Roo.log('GridView.syncScroll');
37875         var sb = this.scroller.dom;
37876         var sh = this.mainHd.dom;
37877         var bs = this.mainBody.dom;
37878         var lv = this.lockedBody.dom;
37879         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37880         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37881     },
37882
37883     handleScroll : function(e){
37884         this.syncScroll();
37885         var sb = this.scroller.dom;
37886         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37887         e.stopEvent();
37888     },
37889
37890     handleWheel : function(e){
37891         var d = e.getWheelDelta();
37892         this.scroller.dom.scrollTop -= d*22;
37893         // set this here to prevent jumpy scrolling on large tables
37894         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37895         e.stopEvent();
37896     },
37897
37898     renderRows : function(startRow, endRow){
37899         // pull in all the crap needed to render rows
37900         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37901         var colCount = cm.getColumnCount();
37902
37903         if(ds.getCount() < 1){
37904             return ["", ""];
37905         }
37906
37907         // build a map for all the columns
37908         var cs = [];
37909         for(var i = 0; i < colCount; i++){
37910             var name = cm.getDataIndex(i);
37911             cs[i] = {
37912                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37913                 renderer : cm.getRenderer(i),
37914                 id : cm.getColumnId(i),
37915                 locked : cm.isLocked(i),
37916                 has_editor : cm.isCellEditable(i)
37917             };
37918         }
37919
37920         startRow = startRow || 0;
37921         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37922
37923         // records to render
37924         var rs = ds.getRange(startRow, endRow);
37925
37926         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37927     },
37928
37929     // As much as I hate to duplicate code, this was branched because FireFox really hates
37930     // [].join("") on strings. The performance difference was substantial enough to
37931     // branch this function
37932     doRender : Roo.isGecko ?
37933             function(cs, rs, ds, startRow, colCount, stripe){
37934                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37935                 // buffers
37936                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37937                 
37938                 var hasListener = this.grid.hasListener('rowclass');
37939                 var rowcfg = {};
37940                 for(var j = 0, len = rs.length; j < len; j++){
37941                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37942                     for(var i = 0; i < colCount; i++){
37943                         c = cs[i];
37944                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37945                         p.id = c.id;
37946                         p.css = p.attr = "";
37947                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37948                         if(p.value == undefined || p.value === "") {
37949                             p.value = "&#160;";
37950                         }
37951                         if(c.has_editor){
37952                             p.css += ' x-grid-editable-cell';
37953                         }
37954                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
37955                             p.css +=  ' x-grid-dirty-cell';
37956                         }
37957                         var markup = ct.apply(p);
37958                         if(!c.locked){
37959                             cb+= markup;
37960                         }else{
37961                             lcb+= markup;
37962                         }
37963                     }
37964                     var alt = [];
37965                     if(stripe && ((rowIndex+1) % 2 == 0)){
37966                         alt.push("x-grid-row-alt")
37967                     }
37968                     if(r.dirty){
37969                         alt.push(  " x-grid-dirty-row");
37970                     }
37971                     rp.cells = lcb;
37972                     if(this.getRowClass){
37973                         alt.push(this.getRowClass(r, rowIndex));
37974                     }
37975                     if (hasListener) {
37976                         rowcfg = {
37977                              
37978                             record: r,
37979                             rowIndex : rowIndex,
37980                             rowClass : ''
37981                         };
37982                         this.grid.fireEvent('rowclass', this, rowcfg);
37983                         alt.push(rowcfg.rowClass);
37984                     }
37985                     rp.alt = alt.join(" ");
37986                     lbuf+= rt.apply(rp);
37987                     rp.cells = cb;
37988                     buf+=  rt.apply(rp);
37989                 }
37990                 return [lbuf, buf];
37991             } :
37992             function(cs, rs, ds, startRow, colCount, stripe){
37993                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37994                 // buffers
37995                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37996                 var hasListener = this.grid.hasListener('rowclass');
37997  
37998                 var rowcfg = {};
37999                 for(var j = 0, len = rs.length; j < len; j++){
38000                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38001                     for(var i = 0; i < colCount; i++){
38002                         c = cs[i];
38003                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38004                         p.id = c.id;
38005                         p.css = p.attr = "";
38006                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38007                         if(p.value == undefined || p.value === "") {
38008                             p.value = "&#160;";
38009                         }
38010                         //Roo.log(c);
38011                          if(c.has_editor){
38012                             p.css += ' x-grid-editable-cell';
38013                         }
38014                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38015                             p.css += ' x-grid-dirty-cell' 
38016                         }
38017                         
38018                         var markup = ct.apply(p);
38019                         if(!c.locked){
38020                             cb[cb.length] = markup;
38021                         }else{
38022                             lcb[lcb.length] = markup;
38023                         }
38024                     }
38025                     var alt = [];
38026                     if(stripe && ((rowIndex+1) % 2 == 0)){
38027                         alt.push( "x-grid-row-alt");
38028                     }
38029                     if(r.dirty){
38030                         alt.push(" x-grid-dirty-row");
38031                     }
38032                     rp.cells = lcb;
38033                     if(this.getRowClass){
38034                         alt.push( this.getRowClass(r, rowIndex));
38035                     }
38036                     if (hasListener) {
38037                         rowcfg = {
38038                              
38039                             record: r,
38040                             rowIndex : rowIndex,
38041                             rowClass : ''
38042                         };
38043                         this.grid.fireEvent('rowclass', this, rowcfg);
38044                         alt.push(rowcfg.rowClass);
38045                     }
38046                     
38047                     rp.alt = alt.join(" ");
38048                     rp.cells = lcb.join("");
38049                     lbuf[lbuf.length] = rt.apply(rp);
38050                     rp.cells = cb.join("");
38051                     buf[buf.length] =  rt.apply(rp);
38052                 }
38053                 return [lbuf.join(""), buf.join("")];
38054             },
38055
38056     renderBody : function(){
38057         var markup = this.renderRows();
38058         var bt = this.templates.body;
38059         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38060     },
38061
38062     /**
38063      * Refreshes the grid
38064      * @param {Boolean} headersToo
38065      */
38066     refresh : function(headersToo){
38067         this.fireEvent("beforerefresh", this);
38068         this.grid.stopEditing();
38069         var result = this.renderBody();
38070         this.lockedBody.update(result[0]);
38071         this.mainBody.update(result[1]);
38072         if(headersToo === true){
38073             this.updateHeaders();
38074             this.updateColumns();
38075             this.updateSplitters();
38076             this.updateHeaderSortState();
38077         }
38078         this.syncRowHeights();
38079         this.layout();
38080         this.fireEvent("refresh", this);
38081     },
38082
38083     handleColumnMove : function(cm, oldIndex, newIndex){
38084         this.indexMap = null;
38085         var s = this.getScrollState();
38086         this.refresh(true);
38087         this.restoreScroll(s);
38088         this.afterMove(newIndex);
38089     },
38090
38091     afterMove : function(colIndex){
38092         if(this.enableMoveAnim && Roo.enableFx){
38093             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38094         }
38095         // if multisort - fix sortOrder, and reload..
38096         if (this.grid.dataSource.multiSort) {
38097             // the we can call sort again..
38098             var dm = this.grid.dataSource;
38099             var cm = this.grid.colModel;
38100             var so = [];
38101             for(var i = 0; i < cm.config.length; i++ ) {
38102                 
38103                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38104                     continue; // dont' bother, it's not in sort list or being set.
38105                 }
38106                 
38107                 so.push(cm.config[i].dataIndex);
38108             };
38109             dm.sortOrder = so;
38110             dm.load(dm.lastOptions);
38111             
38112             
38113         }
38114         
38115     },
38116
38117     updateCell : function(dm, rowIndex, dataIndex){
38118         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38119         if(typeof colIndex == "undefined"){ // not present in grid
38120             return;
38121         }
38122         var cm = this.grid.colModel;
38123         var cell = this.getCell(rowIndex, colIndex);
38124         var cellText = this.getCellText(rowIndex, colIndex);
38125
38126         var p = {
38127             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38128             id : cm.getColumnId(colIndex),
38129             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38130         };
38131         var renderer = cm.getRenderer(colIndex);
38132         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38133         if(typeof val == "undefined" || val === "") {
38134             val = "&#160;";
38135         }
38136         cellText.innerHTML = val;
38137         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38138         this.syncRowHeights(rowIndex, rowIndex);
38139     },
38140
38141     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38142         var maxWidth = 0;
38143         if(this.grid.autoSizeHeaders){
38144             var h = this.getHeaderCellMeasure(colIndex);
38145             maxWidth = Math.max(maxWidth, h.scrollWidth);
38146         }
38147         var tb, index;
38148         if(this.cm.isLocked(colIndex)){
38149             tb = this.getLockedTable();
38150             index = colIndex;
38151         }else{
38152             tb = this.getBodyTable();
38153             index = colIndex - this.cm.getLockedCount();
38154         }
38155         if(tb && tb.rows){
38156             var rows = tb.rows;
38157             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38158             for(var i = 0; i < stopIndex; i++){
38159                 var cell = rows[i].childNodes[index].firstChild;
38160                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38161             }
38162         }
38163         return maxWidth + /*margin for error in IE*/ 5;
38164     },
38165     /**
38166      * Autofit a column to its content.
38167      * @param {Number} colIndex
38168      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38169      */
38170      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38171          if(this.cm.isHidden(colIndex)){
38172              return; // can't calc a hidden column
38173          }
38174         if(forceMinSize){
38175             var cid = this.cm.getColumnId(colIndex);
38176             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38177            if(this.grid.autoSizeHeaders){
38178                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38179            }
38180         }
38181         var newWidth = this.calcColumnWidth(colIndex);
38182         this.cm.setColumnWidth(colIndex,
38183             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38184         if(!suppressEvent){
38185             this.grid.fireEvent("columnresize", colIndex, newWidth);
38186         }
38187     },
38188
38189     /**
38190      * Autofits all columns to their content and then expands to fit any extra space in the grid
38191      */
38192      autoSizeColumns : function(){
38193         var cm = this.grid.colModel;
38194         var colCount = cm.getColumnCount();
38195         for(var i = 0; i < colCount; i++){
38196             this.autoSizeColumn(i, true, true);
38197         }
38198         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38199             this.fitColumns();
38200         }else{
38201             this.updateColumns();
38202             this.layout();
38203         }
38204     },
38205
38206     /**
38207      * Autofits all columns to the grid's width proportionate with their current size
38208      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38209      */
38210     fitColumns : function(reserveScrollSpace){
38211         var cm = this.grid.colModel;
38212         var colCount = cm.getColumnCount();
38213         var cols = [];
38214         var width = 0;
38215         var i, w;
38216         for (i = 0; i < colCount; i++){
38217             if(!cm.isHidden(i) && !cm.isFixed(i)){
38218                 w = cm.getColumnWidth(i);
38219                 cols.push(i);
38220                 cols.push(w);
38221                 width += w;
38222             }
38223         }
38224         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38225         if(reserveScrollSpace){
38226             avail -= 17;
38227         }
38228         var frac = (avail - cm.getTotalWidth())/width;
38229         while (cols.length){
38230             w = cols.pop();
38231             i = cols.pop();
38232             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38233         }
38234         this.updateColumns();
38235         this.layout();
38236     },
38237
38238     onRowSelect : function(rowIndex){
38239         var row = this.getRowComposite(rowIndex);
38240         row.addClass("x-grid-row-selected");
38241     },
38242
38243     onRowDeselect : function(rowIndex){
38244         var row = this.getRowComposite(rowIndex);
38245         row.removeClass("x-grid-row-selected");
38246     },
38247
38248     onCellSelect : function(row, col){
38249         var cell = this.getCell(row, col);
38250         if(cell){
38251             Roo.fly(cell).addClass("x-grid-cell-selected");
38252         }
38253     },
38254
38255     onCellDeselect : function(row, col){
38256         var cell = this.getCell(row, col);
38257         if(cell){
38258             Roo.fly(cell).removeClass("x-grid-cell-selected");
38259         }
38260     },
38261
38262     updateHeaderSortState : function(){
38263         
38264         // sort state can be single { field: xxx, direction : yyy}
38265         // or   { xxx=>ASC , yyy : DESC ..... }
38266         
38267         var mstate = {};
38268         if (!this.ds.multiSort) { 
38269             var state = this.ds.getSortState();
38270             if(!state){
38271                 return;
38272             }
38273             mstate[state.field] = state.direction;
38274             // FIXME... - this is not used here.. but might be elsewhere..
38275             this.sortState = state;
38276             
38277         } else {
38278             mstate = this.ds.sortToggle;
38279         }
38280         //remove existing sort classes..
38281         
38282         var sc = this.sortClasses;
38283         var hds = this.el.select(this.headerSelector).removeClass(sc);
38284         
38285         for(var f in mstate) {
38286         
38287             var sortColumn = this.cm.findColumnIndex(f);
38288             
38289             if(sortColumn != -1){
38290                 var sortDir = mstate[f];        
38291                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38292             }
38293         }
38294         
38295          
38296         
38297     },
38298
38299
38300     handleHeaderClick : function(g, index,e){
38301         
38302         Roo.log("header click");
38303         
38304         if (Roo.isTouch) {
38305             // touch events on header are handled by context
38306             this.handleHdCtx(g,index,e);
38307             return;
38308         }
38309         
38310         
38311         if(this.headersDisabled){
38312             return;
38313         }
38314         var dm = g.dataSource, cm = g.colModel;
38315         if(!cm.isSortable(index)){
38316             return;
38317         }
38318         g.stopEditing();
38319         
38320         if (dm.multiSort) {
38321             // update the sortOrder
38322             var so = [];
38323             for(var i = 0; i < cm.config.length; i++ ) {
38324                 
38325                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38326                     continue; // dont' bother, it's not in sort list or being set.
38327                 }
38328                 
38329                 so.push(cm.config[i].dataIndex);
38330             };
38331             dm.sortOrder = so;
38332         }
38333         
38334         
38335         dm.sort(cm.getDataIndex(index));
38336     },
38337
38338
38339     destroy : function(){
38340         if(this.colMenu){
38341             this.colMenu.removeAll();
38342             Roo.menu.MenuMgr.unregister(this.colMenu);
38343             this.colMenu.getEl().remove();
38344             delete this.colMenu;
38345         }
38346         if(this.hmenu){
38347             this.hmenu.removeAll();
38348             Roo.menu.MenuMgr.unregister(this.hmenu);
38349             this.hmenu.getEl().remove();
38350             delete this.hmenu;
38351         }
38352         if(this.grid.enableColumnMove){
38353             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38354             if(dds){
38355                 for(var dd in dds){
38356                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38357                         var elid = dds[dd].dragElId;
38358                         dds[dd].unreg();
38359                         Roo.get(elid).remove();
38360                     } else if(dds[dd].config.isTarget){
38361                         dds[dd].proxyTop.remove();
38362                         dds[dd].proxyBottom.remove();
38363                         dds[dd].unreg();
38364                     }
38365                     if(Roo.dd.DDM.locationCache[dd]){
38366                         delete Roo.dd.DDM.locationCache[dd];
38367                     }
38368                 }
38369                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38370             }
38371         }
38372         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38373         this.bind(null, null);
38374         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38375     },
38376
38377     handleLockChange : function(){
38378         this.refresh(true);
38379     },
38380
38381     onDenyColumnLock : function(){
38382
38383     },
38384
38385     onDenyColumnHide : function(){
38386
38387     },
38388
38389     handleHdMenuClick : function(item){
38390         var index = this.hdCtxIndex;
38391         var cm = this.cm, ds = this.ds;
38392         switch(item.id){
38393             case "asc":
38394                 ds.sort(cm.getDataIndex(index), "ASC");
38395                 break;
38396             case "desc":
38397                 ds.sort(cm.getDataIndex(index), "DESC");
38398                 break;
38399             case "lock":
38400                 var lc = cm.getLockedCount();
38401                 if(cm.getColumnCount(true) <= lc+1){
38402                     this.onDenyColumnLock();
38403                     return;
38404                 }
38405                 if(lc != index){
38406                     cm.setLocked(index, true, true);
38407                     cm.moveColumn(index, lc);
38408                     this.grid.fireEvent("columnmove", index, lc);
38409                 }else{
38410                     cm.setLocked(index, true);
38411                 }
38412             break;
38413             case "unlock":
38414                 var lc = cm.getLockedCount();
38415                 if((lc-1) != index){
38416                     cm.setLocked(index, false, true);
38417                     cm.moveColumn(index, lc-1);
38418                     this.grid.fireEvent("columnmove", index, lc-1);
38419                 }else{
38420                     cm.setLocked(index, false);
38421                 }
38422             break;
38423             case 'wider': // used to expand cols on touch..
38424             case 'narrow':
38425                 var cw = cm.getColumnWidth(index);
38426                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38427                 cw = Math.max(0, cw);
38428                 cw = Math.min(cw,4000);
38429                 cm.setColumnWidth(index, cw);
38430                 break;
38431                 
38432             default:
38433                 index = cm.getIndexById(item.id.substr(4));
38434                 if(index != -1){
38435                     if(item.checked && cm.getColumnCount(true) <= 1){
38436                         this.onDenyColumnHide();
38437                         return false;
38438                     }
38439                     cm.setHidden(index, item.checked);
38440                 }
38441         }
38442         return true;
38443     },
38444
38445     beforeColMenuShow : function(){
38446         var cm = this.cm,  colCount = cm.getColumnCount();
38447         this.colMenu.removeAll();
38448         for(var i = 0; i < colCount; i++){
38449             this.colMenu.add(new Roo.menu.CheckItem({
38450                 id: "col-"+cm.getColumnId(i),
38451                 text: cm.getColumnHeader(i),
38452                 checked: !cm.isHidden(i),
38453                 hideOnClick:false
38454             }));
38455         }
38456     },
38457
38458     handleHdCtx : function(g, index, e){
38459         e.stopEvent();
38460         var hd = this.getHeaderCell(index);
38461         this.hdCtxIndex = index;
38462         var ms = this.hmenu.items, cm = this.cm;
38463         ms.get("asc").setDisabled(!cm.isSortable(index));
38464         ms.get("desc").setDisabled(!cm.isSortable(index));
38465         if(this.grid.enableColLock !== false){
38466             ms.get("lock").setDisabled(cm.isLocked(index));
38467             ms.get("unlock").setDisabled(!cm.isLocked(index));
38468         }
38469         this.hmenu.show(hd, "tl-bl");
38470     },
38471
38472     handleHdOver : function(e){
38473         var hd = this.findHeaderCell(e.getTarget());
38474         if(hd && !this.headersDisabled){
38475             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38476                this.fly(hd).addClass("x-grid-hd-over");
38477             }
38478         }
38479     },
38480
38481     handleHdOut : function(e){
38482         var hd = this.findHeaderCell(e.getTarget());
38483         if(hd){
38484             this.fly(hd).removeClass("x-grid-hd-over");
38485         }
38486     },
38487
38488     handleSplitDblClick : function(e, t){
38489         var i = this.getCellIndex(t);
38490         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38491             this.autoSizeColumn(i, true);
38492             this.layout();
38493         }
38494     },
38495
38496     render : function(){
38497
38498         var cm = this.cm;
38499         var colCount = cm.getColumnCount();
38500
38501         if(this.grid.monitorWindowResize === true){
38502             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38503         }
38504         var header = this.renderHeaders();
38505         var body = this.templates.body.apply({rows:""});
38506         var html = this.templates.master.apply({
38507             lockedBody: body,
38508             body: body,
38509             lockedHeader: header[0],
38510             header: header[1]
38511         });
38512
38513         //this.updateColumns();
38514
38515         this.grid.getGridEl().dom.innerHTML = html;
38516
38517         this.initElements();
38518         
38519         // a kludge to fix the random scolling effect in webkit
38520         this.el.on("scroll", function() {
38521             this.el.dom.scrollTop=0; // hopefully not recursive..
38522         },this);
38523
38524         this.scroller.on("scroll", this.handleScroll, this);
38525         this.lockedBody.on("mousewheel", this.handleWheel, this);
38526         this.mainBody.on("mousewheel", this.handleWheel, this);
38527
38528         this.mainHd.on("mouseover", this.handleHdOver, this);
38529         this.mainHd.on("mouseout", this.handleHdOut, this);
38530         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38531                 {delegate: "."+this.splitClass});
38532
38533         this.lockedHd.on("mouseover", this.handleHdOver, this);
38534         this.lockedHd.on("mouseout", this.handleHdOut, this);
38535         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38536                 {delegate: "."+this.splitClass});
38537
38538         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38539             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38540         }
38541
38542         this.updateSplitters();
38543
38544         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38545             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38546             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38547         }
38548
38549         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38550             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38551             this.hmenu.add(
38552                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38553                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38554             );
38555             if(this.grid.enableColLock !== false){
38556                 this.hmenu.add('-',
38557                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38558                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38559                 );
38560             }
38561             if (Roo.isTouch) {
38562                  this.hmenu.add('-',
38563                     {id:"wider", text: this.columnsWiderText},
38564                     {id:"narrow", text: this.columnsNarrowText }
38565                 );
38566                 
38567                  
38568             }
38569             
38570             if(this.grid.enableColumnHide !== false){
38571
38572                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38573                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38574                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38575
38576                 this.hmenu.add('-',
38577                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38578                 );
38579             }
38580             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38581
38582             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38583         }
38584
38585         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38586             this.dd = new Roo.grid.GridDragZone(this.grid, {
38587                 ddGroup : this.grid.ddGroup || 'GridDD'
38588             });
38589             
38590         }
38591
38592         /*
38593         for(var i = 0; i < colCount; i++){
38594             if(cm.isHidden(i)){
38595                 this.hideColumn(i);
38596             }
38597             if(cm.config[i].align){
38598                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38599                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38600             }
38601         }*/
38602         
38603         this.updateHeaderSortState();
38604
38605         this.beforeInitialResize();
38606         this.layout(true);
38607
38608         // two part rendering gives faster view to the user
38609         this.renderPhase2.defer(1, this);
38610     },
38611
38612     renderPhase2 : function(){
38613         // render the rows now
38614         this.refresh();
38615         if(this.grid.autoSizeColumns){
38616             this.autoSizeColumns();
38617         }
38618     },
38619
38620     beforeInitialResize : function(){
38621
38622     },
38623
38624     onColumnSplitterMoved : function(i, w){
38625         this.userResized = true;
38626         var cm = this.grid.colModel;
38627         cm.setColumnWidth(i, w, true);
38628         var cid = cm.getColumnId(i);
38629         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38630         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38631         this.updateSplitters();
38632         this.layout();
38633         this.grid.fireEvent("columnresize", i, w);
38634     },
38635
38636     syncRowHeights : function(startIndex, endIndex){
38637         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38638             startIndex = startIndex || 0;
38639             var mrows = this.getBodyTable().rows;
38640             var lrows = this.getLockedTable().rows;
38641             var len = mrows.length-1;
38642             endIndex = Math.min(endIndex || len, len);
38643             for(var i = startIndex; i <= endIndex; i++){
38644                 var m = mrows[i], l = lrows[i];
38645                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38646                 m.style.height = l.style.height = h + "px";
38647             }
38648         }
38649     },
38650
38651     layout : function(initialRender, is2ndPass){
38652         var g = this.grid;
38653         var auto = g.autoHeight;
38654         var scrollOffset = 16;
38655         var c = g.getGridEl(), cm = this.cm,
38656                 expandCol = g.autoExpandColumn,
38657                 gv = this;
38658         //c.beginMeasure();
38659
38660         if(!c.dom.offsetWidth){ // display:none?
38661             if(initialRender){
38662                 this.lockedWrap.show();
38663                 this.mainWrap.show();
38664             }
38665             return;
38666         }
38667
38668         var hasLock = this.cm.isLocked(0);
38669
38670         var tbh = this.headerPanel.getHeight();
38671         var bbh = this.footerPanel.getHeight();
38672
38673         if(auto){
38674             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38675             var newHeight = ch + c.getBorderWidth("tb");
38676             if(g.maxHeight){
38677                 newHeight = Math.min(g.maxHeight, newHeight);
38678             }
38679             c.setHeight(newHeight);
38680         }
38681
38682         if(g.autoWidth){
38683             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38684         }
38685
38686         var s = this.scroller;
38687
38688         var csize = c.getSize(true);
38689
38690         this.el.setSize(csize.width, csize.height);
38691
38692         this.headerPanel.setWidth(csize.width);
38693         this.footerPanel.setWidth(csize.width);
38694
38695         var hdHeight = this.mainHd.getHeight();
38696         var vw = csize.width;
38697         var vh = csize.height - (tbh + bbh);
38698
38699         s.setSize(vw, vh);
38700
38701         var bt = this.getBodyTable();
38702         
38703         if(cm.getLockedCount() == cm.config.length){
38704             bt = this.getLockedTable();
38705         }
38706         
38707         var ltWidth = hasLock ?
38708                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38709
38710         var scrollHeight = bt.offsetHeight;
38711         var scrollWidth = ltWidth + bt.offsetWidth;
38712         var vscroll = false, hscroll = false;
38713
38714         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38715
38716         var lw = this.lockedWrap, mw = this.mainWrap;
38717         var lb = this.lockedBody, mb = this.mainBody;
38718
38719         setTimeout(function(){
38720             var t = s.dom.offsetTop;
38721             var w = s.dom.clientWidth,
38722                 h = s.dom.clientHeight;
38723
38724             lw.setTop(t);
38725             lw.setSize(ltWidth, h);
38726
38727             mw.setLeftTop(ltWidth, t);
38728             mw.setSize(w-ltWidth, h);
38729
38730             lb.setHeight(h-hdHeight);
38731             mb.setHeight(h-hdHeight);
38732
38733             if(is2ndPass !== true && !gv.userResized && expandCol){
38734                 // high speed resize without full column calculation
38735                 
38736                 var ci = cm.getIndexById(expandCol);
38737                 if (ci < 0) {
38738                     ci = cm.findColumnIndex(expandCol);
38739                 }
38740                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38741                 var expandId = cm.getColumnId(ci);
38742                 var  tw = cm.getTotalWidth(false);
38743                 var currentWidth = cm.getColumnWidth(ci);
38744                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38745                 if(currentWidth != cw){
38746                     cm.setColumnWidth(ci, cw, true);
38747                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38748                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38749                     gv.updateSplitters();
38750                     gv.layout(false, true);
38751                 }
38752             }
38753
38754             if(initialRender){
38755                 lw.show();
38756                 mw.show();
38757             }
38758             //c.endMeasure();
38759         }, 10);
38760     },
38761
38762     onWindowResize : function(){
38763         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38764             return;
38765         }
38766         this.layout();
38767     },
38768
38769     appendFooter : function(parentEl){
38770         return null;
38771     },
38772
38773     sortAscText : "Sort Ascending",
38774     sortDescText : "Sort Descending",
38775     lockText : "Lock Column",
38776     unlockText : "Unlock Column",
38777     columnsText : "Columns",
38778  
38779     columnsWiderText : "Wider",
38780     columnsNarrowText : "Thinner"
38781 });
38782
38783
38784 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38785     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38786     this.proxy.el.addClass('x-grid3-col-dd');
38787 };
38788
38789 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38790     handleMouseDown : function(e){
38791
38792     },
38793
38794     callHandleMouseDown : function(e){
38795         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38796     }
38797 });
38798 /*
38799  * Based on:
38800  * Ext JS Library 1.1.1
38801  * Copyright(c) 2006-2007, Ext JS, LLC.
38802  *
38803  * Originally Released Under LGPL - original licence link has changed is not relivant.
38804  *
38805  * Fork - LGPL
38806  * <script type="text/javascript">
38807  */
38808  
38809 // private
38810 // This is a support class used internally by the Grid components
38811 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38812     this.grid = grid;
38813     this.view = grid.getView();
38814     this.proxy = this.view.resizeProxy;
38815     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38816         "gridSplitters" + this.grid.getGridEl().id, {
38817         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38818     });
38819     this.setHandleElId(Roo.id(hd));
38820     this.setOuterHandleElId(Roo.id(hd2));
38821     this.scroll = false;
38822 };
38823 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38824     fly: Roo.Element.fly,
38825
38826     b4StartDrag : function(x, y){
38827         this.view.headersDisabled = true;
38828         this.proxy.setHeight(this.view.mainWrap.getHeight());
38829         var w = this.cm.getColumnWidth(this.cellIndex);
38830         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38831         this.resetConstraints();
38832         this.setXConstraint(minw, 1000);
38833         this.setYConstraint(0, 0);
38834         this.minX = x - minw;
38835         this.maxX = x + 1000;
38836         this.startPos = x;
38837         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38838     },
38839
38840
38841     handleMouseDown : function(e){
38842         ev = Roo.EventObject.setEvent(e);
38843         var t = this.fly(ev.getTarget());
38844         if(t.hasClass("x-grid-split")){
38845             this.cellIndex = this.view.getCellIndex(t.dom);
38846             this.split = t.dom;
38847             this.cm = this.grid.colModel;
38848             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38849                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38850             }
38851         }
38852     },
38853
38854     endDrag : function(e){
38855         this.view.headersDisabled = false;
38856         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38857         var diff = endX - this.startPos;
38858         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38859     },
38860
38861     autoOffset : function(){
38862         this.setDelta(0,0);
38863     }
38864 });/*
38865  * Based on:
38866  * Ext JS Library 1.1.1
38867  * Copyright(c) 2006-2007, Ext JS, LLC.
38868  *
38869  * Originally Released Under LGPL - original licence link has changed is not relivant.
38870  *
38871  * Fork - LGPL
38872  * <script type="text/javascript">
38873  */
38874  
38875 // private
38876 // This is a support class used internally by the Grid components
38877 Roo.grid.GridDragZone = function(grid, config){
38878     this.view = grid.getView();
38879     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38880     if(this.view.lockedBody){
38881         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38882         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38883     }
38884     this.scroll = false;
38885     this.grid = grid;
38886     this.ddel = document.createElement('div');
38887     this.ddel.className = 'x-grid-dd-wrap';
38888 };
38889
38890 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38891     ddGroup : "GridDD",
38892
38893     getDragData : function(e){
38894         var t = Roo.lib.Event.getTarget(e);
38895         var rowIndex = this.view.findRowIndex(t);
38896         var sm = this.grid.selModel;
38897             
38898         //Roo.log(rowIndex);
38899         
38900         if (sm.getSelectedCell) {
38901             // cell selection..
38902             if (!sm.getSelectedCell()) {
38903                 return false;
38904             }
38905             if (rowIndex != sm.getSelectedCell()[0]) {
38906                 return false;
38907             }
38908         
38909         }
38910         
38911         if(rowIndex !== false){
38912             
38913             // if editorgrid.. 
38914             
38915             
38916             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38917                
38918             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38919               //  
38920             //}
38921             if (e.hasModifier()){
38922                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38923             }
38924             
38925             Roo.log("getDragData");
38926             
38927             return {
38928                 grid: this.grid,
38929                 ddel: this.ddel,
38930                 rowIndex: rowIndex,
38931                 selections:sm.getSelections ? sm.getSelections() : (
38932                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38933                 )
38934             };
38935         }
38936         return false;
38937     },
38938
38939     onInitDrag : function(e){
38940         var data = this.dragData;
38941         this.ddel.innerHTML = this.grid.getDragDropText();
38942         this.proxy.update(this.ddel);
38943         // fire start drag?
38944     },
38945
38946     afterRepair : function(){
38947         this.dragging = false;
38948     },
38949
38950     getRepairXY : function(e, data){
38951         return false;
38952     },
38953
38954     onEndDrag : function(data, e){
38955         // fire end drag?
38956     },
38957
38958     onValidDrop : function(dd, e, id){
38959         // fire drag drop?
38960         this.hideProxy();
38961     },
38962
38963     beforeInvalidDrop : function(e, id){
38964
38965     }
38966 });/*
38967  * Based on:
38968  * Ext JS Library 1.1.1
38969  * Copyright(c) 2006-2007, Ext JS, LLC.
38970  *
38971  * Originally Released Under LGPL - original licence link has changed is not relivant.
38972  *
38973  * Fork - LGPL
38974  * <script type="text/javascript">
38975  */
38976  
38977
38978 /**
38979  * @class Roo.grid.ColumnModel
38980  * @extends Roo.util.Observable
38981  * This is the default implementation of a ColumnModel used by the Grid. It defines
38982  * the columns in the grid.
38983  * <br>Usage:<br>
38984  <pre><code>
38985  var colModel = new Roo.grid.ColumnModel([
38986         {header: "Ticker", width: 60, sortable: true, locked: true},
38987         {header: "Company Name", width: 150, sortable: true},
38988         {header: "Market Cap.", width: 100, sortable: true},
38989         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38990         {header: "Employees", width: 100, sortable: true, resizable: false}
38991  ]);
38992  </code></pre>
38993  * <p>
38994  
38995  * The config options listed for this class are options which may appear in each
38996  * individual column definition.
38997  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38998  * @constructor
38999  * @param {Object} config An Array of column config objects. See this class's
39000  * config objects for details.
39001 */
39002 Roo.grid.ColumnModel = function(config){
39003         /**
39004      * The config passed into the constructor
39005      */
39006     this.config = config;
39007     this.lookup = {};
39008
39009     // if no id, create one
39010     // if the column does not have a dataIndex mapping,
39011     // map it to the order it is in the config
39012     for(var i = 0, len = config.length; i < len; i++){
39013         var c = config[i];
39014         if(typeof c.dataIndex == "undefined"){
39015             c.dataIndex = i;
39016         }
39017         if(typeof c.renderer == "string"){
39018             c.renderer = Roo.util.Format[c.renderer];
39019         }
39020         if(typeof c.id == "undefined"){
39021             c.id = Roo.id();
39022         }
39023         if(c.editor && c.editor.xtype){
39024             c.editor  = Roo.factory(c.editor, Roo.grid);
39025         }
39026         if(c.editor && c.editor.isFormField){
39027             c.editor = new Roo.grid.GridEditor(c.editor);
39028         }
39029         this.lookup[c.id] = c;
39030     }
39031
39032     /**
39033      * The width of columns which have no width specified (defaults to 100)
39034      * @type Number
39035      */
39036     this.defaultWidth = 100;
39037
39038     /**
39039      * Default sortable of columns which have no sortable specified (defaults to false)
39040      * @type Boolean
39041      */
39042     this.defaultSortable = false;
39043
39044     this.addEvents({
39045         /**
39046              * @event widthchange
39047              * Fires when the width of a column changes.
39048              * @param {ColumnModel} this
39049              * @param {Number} columnIndex The column index
39050              * @param {Number} newWidth The new width
39051              */
39052             "widthchange": true,
39053         /**
39054              * @event headerchange
39055              * Fires when the text of a header changes.
39056              * @param {ColumnModel} this
39057              * @param {Number} columnIndex The column index
39058              * @param {Number} newText The new header text
39059              */
39060             "headerchange": true,
39061         /**
39062              * @event hiddenchange
39063              * Fires when a column is hidden or "unhidden".
39064              * @param {ColumnModel} this
39065              * @param {Number} columnIndex The column index
39066              * @param {Boolean} hidden true if hidden, false otherwise
39067              */
39068             "hiddenchange": true,
39069             /**
39070          * @event columnmoved
39071          * Fires when a column is moved.
39072          * @param {ColumnModel} this
39073          * @param {Number} oldIndex
39074          * @param {Number} newIndex
39075          */
39076         "columnmoved" : true,
39077         /**
39078          * @event columlockchange
39079          * Fires when a column's locked state is changed
39080          * @param {ColumnModel} this
39081          * @param {Number} colIndex
39082          * @param {Boolean} locked true if locked
39083          */
39084         "columnlockchange" : true
39085     });
39086     Roo.grid.ColumnModel.superclass.constructor.call(this);
39087 };
39088 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39089     /**
39090      * @cfg {String} header The header text to display in the Grid view.
39091      */
39092     /**
39093      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39094      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39095      * specified, the column's index is used as an index into the Record's data Array.
39096      */
39097     /**
39098      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39099      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39100      */
39101     /**
39102      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39103      * Defaults to the value of the {@link #defaultSortable} property.
39104      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39105      */
39106     /**
39107      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39108      */
39109     /**
39110      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39111      */
39112     /**
39113      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39114      */
39115     /**
39116      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39117      */
39118     /**
39119      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39120      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39121      * default renderer uses the raw data value. If an object is returned (bootstrap only)
39122      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39123      */
39124        /**
39125      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39126      */
39127     /**
39128      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39129      */
39130     /**
39131      * @cfg {String} cursor (Optional)
39132      */
39133     /**
39134      * @cfg {String} tooltip (Optional)
39135      */
39136     /**
39137      * @cfg {Number} xs (Optional)
39138      */
39139     /**
39140      * @cfg {Number} sm (Optional)
39141      */
39142     /**
39143      * @cfg {Number} md (Optional)
39144      */
39145     /**
39146      * @cfg {Number} lg (Optional)
39147      */
39148     /**
39149      * Returns the id of the column at the specified index.
39150      * @param {Number} index The column index
39151      * @return {String} the id
39152      */
39153     getColumnId : function(index){
39154         return this.config[index].id;
39155     },
39156
39157     /**
39158      * Returns the column for a specified id.
39159      * @param {String} id The column id
39160      * @return {Object} the column
39161      */
39162     getColumnById : function(id){
39163         return this.lookup[id];
39164     },
39165
39166     
39167     /**
39168      * Returns the column for a specified dataIndex.
39169      * @param {String} dataIndex The column dataIndex
39170      * @return {Object|Boolean} the column or false if not found
39171      */
39172     getColumnByDataIndex: function(dataIndex){
39173         var index = this.findColumnIndex(dataIndex);
39174         return index > -1 ? this.config[index] : false;
39175     },
39176     
39177     /**
39178      * Returns the index for a specified column id.
39179      * @param {String} id The column id
39180      * @return {Number} the index, or -1 if not found
39181      */
39182     getIndexById : function(id){
39183         for(var i = 0, len = this.config.length; i < len; i++){
39184             if(this.config[i].id == id){
39185                 return i;
39186             }
39187         }
39188         return -1;
39189     },
39190     
39191     /**
39192      * Returns the index for a specified column dataIndex.
39193      * @param {String} dataIndex The column dataIndex
39194      * @return {Number} the index, or -1 if not found
39195      */
39196     
39197     findColumnIndex : function(dataIndex){
39198         for(var i = 0, len = this.config.length; i < len; i++){
39199             if(this.config[i].dataIndex == dataIndex){
39200                 return i;
39201             }
39202         }
39203         return -1;
39204     },
39205     
39206     
39207     moveColumn : function(oldIndex, newIndex){
39208         var c = this.config[oldIndex];
39209         this.config.splice(oldIndex, 1);
39210         this.config.splice(newIndex, 0, c);
39211         this.dataMap = null;
39212         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39213     },
39214
39215     isLocked : function(colIndex){
39216         return this.config[colIndex].locked === true;
39217     },
39218
39219     setLocked : function(colIndex, value, suppressEvent){
39220         if(this.isLocked(colIndex) == value){
39221             return;
39222         }
39223         this.config[colIndex].locked = value;
39224         if(!suppressEvent){
39225             this.fireEvent("columnlockchange", this, colIndex, value);
39226         }
39227     },
39228
39229     getTotalLockedWidth : function(){
39230         var totalWidth = 0;
39231         for(var i = 0; i < this.config.length; i++){
39232             if(this.isLocked(i) && !this.isHidden(i)){
39233                 this.totalWidth += this.getColumnWidth(i);
39234             }
39235         }
39236         return totalWidth;
39237     },
39238
39239     getLockedCount : function(){
39240         for(var i = 0, len = this.config.length; i < len; i++){
39241             if(!this.isLocked(i)){
39242                 return i;
39243             }
39244         }
39245         
39246         return this.config.length;
39247     },
39248
39249     /**
39250      * Returns the number of columns.
39251      * @return {Number}
39252      */
39253     getColumnCount : function(visibleOnly){
39254         if(visibleOnly === true){
39255             var c = 0;
39256             for(var i = 0, len = this.config.length; i < len; i++){
39257                 if(!this.isHidden(i)){
39258                     c++;
39259                 }
39260             }
39261             return c;
39262         }
39263         return this.config.length;
39264     },
39265
39266     /**
39267      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39268      * @param {Function} fn
39269      * @param {Object} scope (optional)
39270      * @return {Array} result
39271      */
39272     getColumnsBy : function(fn, scope){
39273         var r = [];
39274         for(var i = 0, len = this.config.length; i < len; i++){
39275             var c = this.config[i];
39276             if(fn.call(scope||this, c, i) === true){
39277                 r[r.length] = c;
39278             }
39279         }
39280         return r;
39281     },
39282
39283     /**
39284      * Returns true if the specified column is sortable.
39285      * @param {Number} col The column index
39286      * @return {Boolean}
39287      */
39288     isSortable : function(col){
39289         if(typeof this.config[col].sortable == "undefined"){
39290             return this.defaultSortable;
39291         }
39292         return this.config[col].sortable;
39293     },
39294
39295     /**
39296      * Returns the rendering (formatting) function defined for the column.
39297      * @param {Number} col The column index.
39298      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39299      */
39300     getRenderer : function(col){
39301         if(!this.config[col].renderer){
39302             return Roo.grid.ColumnModel.defaultRenderer;
39303         }
39304         return this.config[col].renderer;
39305     },
39306
39307     /**
39308      * Sets the rendering (formatting) function for a column.
39309      * @param {Number} col The column index
39310      * @param {Function} fn The function to use to process the cell's raw data
39311      * to return HTML markup for the grid view. The render function is called with
39312      * the following parameters:<ul>
39313      * <li>Data value.</li>
39314      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39315      * <li>css A CSS style string to apply to the table cell.</li>
39316      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39317      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39318      * <li>Row index</li>
39319      * <li>Column index</li>
39320      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39321      */
39322     setRenderer : function(col, fn){
39323         this.config[col].renderer = fn;
39324     },
39325
39326     /**
39327      * Returns the width for the specified column.
39328      * @param {Number} col The column index
39329      * @return {Number}
39330      */
39331     getColumnWidth : function(col){
39332         return this.config[col].width * 1 || this.defaultWidth;
39333     },
39334
39335     /**
39336      * Sets the width for a column.
39337      * @param {Number} col The column index
39338      * @param {Number} width The new width
39339      */
39340     setColumnWidth : function(col, width, suppressEvent){
39341         this.config[col].width = width;
39342         this.totalWidth = null;
39343         if(!suppressEvent){
39344              this.fireEvent("widthchange", this, col, width);
39345         }
39346     },
39347
39348     /**
39349      * Returns the total width of all columns.
39350      * @param {Boolean} includeHidden True to include hidden column widths
39351      * @return {Number}
39352      */
39353     getTotalWidth : function(includeHidden){
39354         if(!this.totalWidth){
39355             this.totalWidth = 0;
39356             for(var i = 0, len = this.config.length; i < len; i++){
39357                 if(includeHidden || !this.isHidden(i)){
39358                     this.totalWidth += this.getColumnWidth(i);
39359                 }
39360             }
39361         }
39362         return this.totalWidth;
39363     },
39364
39365     /**
39366      * Returns the header for the specified column.
39367      * @param {Number} col The column index
39368      * @return {String}
39369      */
39370     getColumnHeader : function(col){
39371         return this.config[col].header;
39372     },
39373
39374     /**
39375      * Sets the header for a column.
39376      * @param {Number} col The column index
39377      * @param {String} header The new header
39378      */
39379     setColumnHeader : function(col, header){
39380         this.config[col].header = header;
39381         this.fireEvent("headerchange", this, col, header);
39382     },
39383
39384     /**
39385      * Returns the tooltip for the specified column.
39386      * @param {Number} col The column index
39387      * @return {String}
39388      */
39389     getColumnTooltip : function(col){
39390             return this.config[col].tooltip;
39391     },
39392     /**
39393      * Sets the tooltip for a column.
39394      * @param {Number} col The column index
39395      * @param {String} tooltip The new tooltip
39396      */
39397     setColumnTooltip : function(col, tooltip){
39398             this.config[col].tooltip = tooltip;
39399     },
39400
39401     /**
39402      * Returns the dataIndex for the specified column.
39403      * @param {Number} col The column index
39404      * @return {Number}
39405      */
39406     getDataIndex : function(col){
39407         return this.config[col].dataIndex;
39408     },
39409
39410     /**
39411      * Sets the dataIndex for a column.
39412      * @param {Number} col The column index
39413      * @param {Number} dataIndex The new dataIndex
39414      */
39415     setDataIndex : function(col, dataIndex){
39416         this.config[col].dataIndex = dataIndex;
39417     },
39418
39419     
39420     
39421     /**
39422      * Returns true if the cell is editable.
39423      * @param {Number} colIndex The column index
39424      * @param {Number} rowIndex The row index - this is nto actually used..?
39425      * @return {Boolean}
39426      */
39427     isCellEditable : function(colIndex, rowIndex){
39428         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39429     },
39430
39431     /**
39432      * Returns the editor defined for the cell/column.
39433      * return false or null to disable editing.
39434      * @param {Number} colIndex The column index
39435      * @param {Number} rowIndex The row index
39436      * @return {Object}
39437      */
39438     getCellEditor : function(colIndex, rowIndex){
39439         return this.config[colIndex].editor;
39440     },
39441
39442     /**
39443      * Sets if a column is editable.
39444      * @param {Number} col The column index
39445      * @param {Boolean} editable True if the column is editable
39446      */
39447     setEditable : function(col, editable){
39448         this.config[col].editable = editable;
39449     },
39450
39451
39452     /**
39453      * Returns true if the column is hidden.
39454      * @param {Number} colIndex The column index
39455      * @return {Boolean}
39456      */
39457     isHidden : function(colIndex){
39458         return this.config[colIndex].hidden;
39459     },
39460
39461
39462     /**
39463      * Returns true if the column width cannot be changed
39464      */
39465     isFixed : function(colIndex){
39466         return this.config[colIndex].fixed;
39467     },
39468
39469     /**
39470      * Returns true if the column can be resized
39471      * @return {Boolean}
39472      */
39473     isResizable : function(colIndex){
39474         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39475     },
39476     /**
39477      * Sets if a column is hidden.
39478      * @param {Number} colIndex The column index
39479      * @param {Boolean} hidden True if the column is hidden
39480      */
39481     setHidden : function(colIndex, hidden){
39482         this.config[colIndex].hidden = hidden;
39483         this.totalWidth = null;
39484         this.fireEvent("hiddenchange", this, colIndex, hidden);
39485     },
39486
39487     /**
39488      * Sets the editor for a column.
39489      * @param {Number} col The column index
39490      * @param {Object} editor The editor object
39491      */
39492     setEditor : function(col, editor){
39493         this.config[col].editor = editor;
39494     }
39495 });
39496
39497 Roo.grid.ColumnModel.defaultRenderer = function(value){
39498         if(typeof value == "string" && value.length < 1){
39499             return "&#160;";
39500         }
39501         return value;
39502 };
39503
39504 // Alias for backwards compatibility
39505 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39506 /*
39507  * Based on:
39508  * Ext JS Library 1.1.1
39509  * Copyright(c) 2006-2007, Ext JS, LLC.
39510  *
39511  * Originally Released Under LGPL - original licence link has changed is not relivant.
39512  *
39513  * Fork - LGPL
39514  * <script type="text/javascript">
39515  */
39516
39517 /**
39518  * @class Roo.grid.AbstractSelectionModel
39519  * @extends Roo.util.Observable
39520  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39521  * implemented by descendant classes.  This class should not be directly instantiated.
39522  * @constructor
39523  */
39524 Roo.grid.AbstractSelectionModel = function(){
39525     this.locked = false;
39526     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39527 };
39528
39529 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39530     /** @ignore Called by the grid automatically. Do not call directly. */
39531     init : function(grid){
39532         this.grid = grid;
39533         this.initEvents();
39534     },
39535
39536     /**
39537      * Locks the selections.
39538      */
39539     lock : function(){
39540         this.locked = true;
39541     },
39542
39543     /**
39544      * Unlocks the selections.
39545      */
39546     unlock : function(){
39547         this.locked = false;
39548     },
39549
39550     /**
39551      * Returns true if the selections are locked.
39552      * @return {Boolean}
39553      */
39554     isLocked : function(){
39555         return this.locked;
39556     }
39557 });/*
39558  * Based on:
39559  * Ext JS Library 1.1.1
39560  * Copyright(c) 2006-2007, Ext JS, LLC.
39561  *
39562  * Originally Released Under LGPL - original licence link has changed is not relivant.
39563  *
39564  * Fork - LGPL
39565  * <script type="text/javascript">
39566  */
39567 /**
39568  * @extends Roo.grid.AbstractSelectionModel
39569  * @class Roo.grid.RowSelectionModel
39570  * The default SelectionModel used by {@link Roo.grid.Grid}.
39571  * It supports multiple selections and keyboard selection/navigation. 
39572  * @constructor
39573  * @param {Object} config
39574  */
39575 Roo.grid.RowSelectionModel = function(config){
39576     Roo.apply(this, config);
39577     this.selections = new Roo.util.MixedCollection(false, function(o){
39578         return o.id;
39579     });
39580
39581     this.last = false;
39582     this.lastActive = false;
39583
39584     this.addEvents({
39585         /**
39586              * @event selectionchange
39587              * Fires when the selection changes
39588              * @param {SelectionModel} this
39589              */
39590             "selectionchange" : true,
39591         /**
39592              * @event afterselectionchange
39593              * Fires after the selection changes (eg. by key press or clicking)
39594              * @param {SelectionModel} this
39595              */
39596             "afterselectionchange" : true,
39597         /**
39598              * @event beforerowselect
39599              * Fires when a row is selected being selected, return false to cancel.
39600              * @param {SelectionModel} this
39601              * @param {Number} rowIndex The selected index
39602              * @param {Boolean} keepExisting False if other selections will be cleared
39603              */
39604             "beforerowselect" : true,
39605         /**
39606              * @event rowselect
39607              * Fires when a row is selected.
39608              * @param {SelectionModel} this
39609              * @param {Number} rowIndex The selected index
39610              * @param {Roo.data.Record} r The record
39611              */
39612             "rowselect" : true,
39613         /**
39614              * @event rowdeselect
39615              * Fires when a row is deselected.
39616              * @param {SelectionModel} this
39617              * @param {Number} rowIndex The selected index
39618              */
39619         "rowdeselect" : true
39620     });
39621     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39622     this.locked = false;
39623 };
39624
39625 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39626     /**
39627      * @cfg {Boolean} singleSelect
39628      * True to allow selection of only one row at a time (defaults to false)
39629      */
39630     singleSelect : false,
39631
39632     // private
39633     initEvents : function(){
39634
39635         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39636             this.grid.on("mousedown", this.handleMouseDown, this);
39637         }else{ // allow click to work like normal
39638             this.grid.on("rowclick", this.handleDragableRowClick, this);
39639         }
39640
39641         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39642             "up" : function(e){
39643                 if(!e.shiftKey){
39644                     this.selectPrevious(e.shiftKey);
39645                 }else if(this.last !== false && this.lastActive !== false){
39646                     var last = this.last;
39647                     this.selectRange(this.last,  this.lastActive-1);
39648                     this.grid.getView().focusRow(this.lastActive);
39649                     if(last !== false){
39650                         this.last = last;
39651                     }
39652                 }else{
39653                     this.selectFirstRow();
39654                 }
39655                 this.fireEvent("afterselectionchange", this);
39656             },
39657             "down" : function(e){
39658                 if(!e.shiftKey){
39659                     this.selectNext(e.shiftKey);
39660                 }else if(this.last !== false && this.lastActive !== false){
39661                     var last = this.last;
39662                     this.selectRange(this.last,  this.lastActive+1);
39663                     this.grid.getView().focusRow(this.lastActive);
39664                     if(last !== false){
39665                         this.last = last;
39666                     }
39667                 }else{
39668                     this.selectFirstRow();
39669                 }
39670                 this.fireEvent("afterselectionchange", this);
39671             },
39672             scope: this
39673         });
39674
39675         var view = this.grid.view;
39676         view.on("refresh", this.onRefresh, this);
39677         view.on("rowupdated", this.onRowUpdated, this);
39678         view.on("rowremoved", this.onRemove, this);
39679     },
39680
39681     // private
39682     onRefresh : function(){
39683         var ds = this.grid.dataSource, i, v = this.grid.view;
39684         var s = this.selections;
39685         s.each(function(r){
39686             if((i = ds.indexOfId(r.id)) != -1){
39687                 v.onRowSelect(i);
39688                 s.add(ds.getAt(i)); // updating the selection relate data
39689             }else{
39690                 s.remove(r);
39691             }
39692         });
39693     },
39694
39695     // private
39696     onRemove : function(v, index, r){
39697         this.selections.remove(r);
39698     },
39699
39700     // private
39701     onRowUpdated : function(v, index, r){
39702         if(this.isSelected(r)){
39703             v.onRowSelect(index);
39704         }
39705     },
39706
39707     /**
39708      * Select records.
39709      * @param {Array} records The records to select
39710      * @param {Boolean} keepExisting (optional) True to keep existing selections
39711      */
39712     selectRecords : function(records, keepExisting){
39713         if(!keepExisting){
39714             this.clearSelections();
39715         }
39716         var ds = this.grid.dataSource;
39717         for(var i = 0, len = records.length; i < len; i++){
39718             this.selectRow(ds.indexOf(records[i]), true);
39719         }
39720     },
39721
39722     /**
39723      * Gets the number of selected rows.
39724      * @return {Number}
39725      */
39726     getCount : function(){
39727         return this.selections.length;
39728     },
39729
39730     /**
39731      * Selects the first row in the grid.
39732      */
39733     selectFirstRow : function(){
39734         this.selectRow(0);
39735     },
39736
39737     /**
39738      * Select the last row.
39739      * @param {Boolean} keepExisting (optional) True to keep existing selections
39740      */
39741     selectLastRow : function(keepExisting){
39742         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39743     },
39744
39745     /**
39746      * Selects the row immediately following the last selected row.
39747      * @param {Boolean} keepExisting (optional) True to keep existing selections
39748      */
39749     selectNext : function(keepExisting){
39750         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39751             this.selectRow(this.last+1, keepExisting);
39752             this.grid.getView().focusRow(this.last);
39753         }
39754     },
39755
39756     /**
39757      * Selects the row that precedes the last selected row.
39758      * @param {Boolean} keepExisting (optional) True to keep existing selections
39759      */
39760     selectPrevious : function(keepExisting){
39761         if(this.last){
39762             this.selectRow(this.last-1, keepExisting);
39763             this.grid.getView().focusRow(this.last);
39764         }
39765     },
39766
39767     /**
39768      * Returns the selected records
39769      * @return {Array} Array of selected records
39770      */
39771     getSelections : function(){
39772         return [].concat(this.selections.items);
39773     },
39774
39775     /**
39776      * Returns the first selected record.
39777      * @return {Record}
39778      */
39779     getSelected : function(){
39780         return this.selections.itemAt(0);
39781     },
39782
39783
39784     /**
39785      * Clears all selections.
39786      */
39787     clearSelections : function(fast){
39788         if(this.locked) {
39789             return;
39790         }
39791         if(fast !== true){
39792             var ds = this.grid.dataSource;
39793             var s = this.selections;
39794             s.each(function(r){
39795                 this.deselectRow(ds.indexOfId(r.id));
39796             }, this);
39797             s.clear();
39798         }else{
39799             this.selections.clear();
39800         }
39801         this.last = false;
39802     },
39803
39804
39805     /**
39806      * Selects all rows.
39807      */
39808     selectAll : function(){
39809         if(this.locked) {
39810             return;
39811         }
39812         this.selections.clear();
39813         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39814             this.selectRow(i, true);
39815         }
39816     },
39817
39818     /**
39819      * Returns True if there is a selection.
39820      * @return {Boolean}
39821      */
39822     hasSelection : function(){
39823         return this.selections.length > 0;
39824     },
39825
39826     /**
39827      * Returns True if the specified row is selected.
39828      * @param {Number/Record} record The record or index of the record to check
39829      * @return {Boolean}
39830      */
39831     isSelected : function(index){
39832         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39833         return (r && this.selections.key(r.id) ? true : false);
39834     },
39835
39836     /**
39837      * Returns True if the specified record id is selected.
39838      * @param {String} id The id of record to check
39839      * @return {Boolean}
39840      */
39841     isIdSelected : function(id){
39842         return (this.selections.key(id) ? true : false);
39843     },
39844
39845     // private
39846     handleMouseDown : function(e, t){
39847         var view = this.grid.getView(), rowIndex;
39848         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39849             return;
39850         };
39851         if(e.shiftKey && this.last !== false){
39852             var last = this.last;
39853             this.selectRange(last, rowIndex, e.ctrlKey);
39854             this.last = last; // reset the last
39855             view.focusRow(rowIndex);
39856         }else{
39857             var isSelected = this.isSelected(rowIndex);
39858             if(e.button !== 0 && isSelected){
39859                 view.focusRow(rowIndex);
39860             }else if(e.ctrlKey && isSelected){
39861                 this.deselectRow(rowIndex);
39862             }else if(!isSelected){
39863                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39864                 view.focusRow(rowIndex);
39865             }
39866         }
39867         this.fireEvent("afterselectionchange", this);
39868     },
39869     // private
39870     handleDragableRowClick :  function(grid, rowIndex, e) 
39871     {
39872         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39873             this.selectRow(rowIndex, false);
39874             grid.view.focusRow(rowIndex);
39875              this.fireEvent("afterselectionchange", this);
39876         }
39877     },
39878     
39879     /**
39880      * Selects multiple rows.
39881      * @param {Array} rows Array of the indexes of the row to select
39882      * @param {Boolean} keepExisting (optional) True to keep existing selections
39883      */
39884     selectRows : function(rows, keepExisting){
39885         if(!keepExisting){
39886             this.clearSelections();
39887         }
39888         for(var i = 0, len = rows.length; i < len; i++){
39889             this.selectRow(rows[i], true);
39890         }
39891     },
39892
39893     /**
39894      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39895      * @param {Number} startRow The index of the first row in the range
39896      * @param {Number} endRow The index of the last row in the range
39897      * @param {Boolean} keepExisting (optional) True to retain existing selections
39898      */
39899     selectRange : function(startRow, endRow, keepExisting){
39900         if(this.locked) {
39901             return;
39902         }
39903         if(!keepExisting){
39904             this.clearSelections();
39905         }
39906         if(startRow <= endRow){
39907             for(var i = startRow; i <= endRow; i++){
39908                 this.selectRow(i, true);
39909             }
39910         }else{
39911             for(var i = startRow; i >= endRow; i--){
39912                 this.selectRow(i, true);
39913             }
39914         }
39915     },
39916
39917     /**
39918      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39919      * @param {Number} startRow The index of the first row in the range
39920      * @param {Number} endRow The index of the last row in the range
39921      */
39922     deselectRange : function(startRow, endRow, preventViewNotify){
39923         if(this.locked) {
39924             return;
39925         }
39926         for(var i = startRow; i <= endRow; i++){
39927             this.deselectRow(i, preventViewNotify);
39928         }
39929     },
39930
39931     /**
39932      * Selects a row.
39933      * @param {Number} row The index of the row to select
39934      * @param {Boolean} keepExisting (optional) True to keep existing selections
39935      */
39936     selectRow : function(index, keepExisting, preventViewNotify){
39937         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
39938             return;
39939         }
39940         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39941             if(!keepExisting || this.singleSelect){
39942                 this.clearSelections();
39943             }
39944             var r = this.grid.dataSource.getAt(index);
39945             this.selections.add(r);
39946             this.last = this.lastActive = index;
39947             if(!preventViewNotify){
39948                 this.grid.getView().onRowSelect(index);
39949             }
39950             this.fireEvent("rowselect", this, index, r);
39951             this.fireEvent("selectionchange", this);
39952         }
39953     },
39954
39955     /**
39956      * Deselects a row.
39957      * @param {Number} row The index of the row to deselect
39958      */
39959     deselectRow : function(index, preventViewNotify){
39960         if(this.locked) {
39961             return;
39962         }
39963         if(this.last == index){
39964             this.last = false;
39965         }
39966         if(this.lastActive == index){
39967             this.lastActive = false;
39968         }
39969         var r = this.grid.dataSource.getAt(index);
39970         this.selections.remove(r);
39971         if(!preventViewNotify){
39972             this.grid.getView().onRowDeselect(index);
39973         }
39974         this.fireEvent("rowdeselect", this, index);
39975         this.fireEvent("selectionchange", this);
39976     },
39977
39978     // private
39979     restoreLast : function(){
39980         if(this._last){
39981             this.last = this._last;
39982         }
39983     },
39984
39985     // private
39986     acceptsNav : function(row, col, cm){
39987         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39988     },
39989
39990     // private
39991     onEditorKey : function(field, e){
39992         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39993         if(k == e.TAB){
39994             e.stopEvent();
39995             ed.completeEdit();
39996             if(e.shiftKey){
39997                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39998             }else{
39999                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40000             }
40001         }else if(k == e.ENTER && !e.ctrlKey){
40002             e.stopEvent();
40003             ed.completeEdit();
40004             if(e.shiftKey){
40005                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40006             }else{
40007                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40008             }
40009         }else if(k == e.ESC){
40010             ed.cancelEdit();
40011         }
40012         if(newCell){
40013             g.startEditing(newCell[0], newCell[1]);
40014         }
40015     }
40016 });/*
40017  * Based on:
40018  * Ext JS Library 1.1.1
40019  * Copyright(c) 2006-2007, Ext JS, LLC.
40020  *
40021  * Originally Released Under LGPL - original licence link has changed is not relivant.
40022  *
40023  * Fork - LGPL
40024  * <script type="text/javascript">
40025  */
40026 /**
40027  * @class Roo.grid.CellSelectionModel
40028  * @extends Roo.grid.AbstractSelectionModel
40029  * This class provides the basic implementation for cell selection in a grid.
40030  * @constructor
40031  * @param {Object} config The object containing the configuration of this model.
40032  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40033  */
40034 Roo.grid.CellSelectionModel = function(config){
40035     Roo.apply(this, config);
40036
40037     this.selection = null;
40038
40039     this.addEvents({
40040         /**
40041              * @event beforerowselect
40042              * Fires before a cell is selected.
40043              * @param {SelectionModel} this
40044              * @param {Number} rowIndex The selected row index
40045              * @param {Number} colIndex The selected cell index
40046              */
40047             "beforecellselect" : true,
40048         /**
40049              * @event cellselect
40050              * Fires when a cell is selected.
40051              * @param {SelectionModel} this
40052              * @param {Number} rowIndex The selected row index
40053              * @param {Number} colIndex The selected cell index
40054              */
40055             "cellselect" : true,
40056         /**
40057              * @event selectionchange
40058              * Fires when the active selection changes.
40059              * @param {SelectionModel} this
40060              * @param {Object} selection null for no selection or an object (o) with two properties
40061                 <ul>
40062                 <li>o.record: the record object for the row the selection is in</li>
40063                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40064                 </ul>
40065              */
40066             "selectionchange" : true,
40067         /**
40068              * @event tabend
40069              * Fires when the tab (or enter) was pressed on the last editable cell
40070              * You can use this to trigger add new row.
40071              * @param {SelectionModel} this
40072              */
40073             "tabend" : true,
40074          /**
40075              * @event beforeeditnext
40076              * Fires before the next editable sell is made active
40077              * You can use this to skip to another cell or fire the tabend
40078              *    if you set cell to false
40079              * @param {Object} eventdata object : { cell : [ row, col ] } 
40080              */
40081             "beforeeditnext" : true
40082     });
40083     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40084 };
40085
40086 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40087     
40088     enter_is_tab: false,
40089
40090     /** @ignore */
40091     initEvents : function(){
40092         this.grid.on("mousedown", this.handleMouseDown, this);
40093         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40094         var view = this.grid.view;
40095         view.on("refresh", this.onViewChange, this);
40096         view.on("rowupdated", this.onRowUpdated, this);
40097         view.on("beforerowremoved", this.clearSelections, this);
40098         view.on("beforerowsinserted", this.clearSelections, this);
40099         if(this.grid.isEditor){
40100             this.grid.on("beforeedit", this.beforeEdit,  this);
40101         }
40102     },
40103
40104         //private
40105     beforeEdit : function(e){
40106         this.select(e.row, e.column, false, true, e.record);
40107     },
40108
40109         //private
40110     onRowUpdated : function(v, index, r){
40111         if(this.selection && this.selection.record == r){
40112             v.onCellSelect(index, this.selection.cell[1]);
40113         }
40114     },
40115
40116         //private
40117     onViewChange : function(){
40118         this.clearSelections(true);
40119     },
40120
40121         /**
40122          * Returns the currently selected cell,.
40123          * @return {Array} The selected cell (row, column) or null if none selected.
40124          */
40125     getSelectedCell : function(){
40126         return this.selection ? this.selection.cell : null;
40127     },
40128
40129     /**
40130      * Clears all selections.
40131      * @param {Boolean} true to prevent the gridview from being notified about the change.
40132      */
40133     clearSelections : function(preventNotify){
40134         var s = this.selection;
40135         if(s){
40136             if(preventNotify !== true){
40137                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40138             }
40139             this.selection = null;
40140             this.fireEvent("selectionchange", this, null);
40141         }
40142     },
40143
40144     /**
40145      * Returns true if there is a selection.
40146      * @return {Boolean}
40147      */
40148     hasSelection : function(){
40149         return this.selection ? true : false;
40150     },
40151
40152     /** @ignore */
40153     handleMouseDown : function(e, t){
40154         var v = this.grid.getView();
40155         if(this.isLocked()){
40156             return;
40157         };
40158         var row = v.findRowIndex(t);
40159         var cell = v.findCellIndex(t);
40160         if(row !== false && cell !== false){
40161             this.select(row, cell);
40162         }
40163     },
40164
40165     /**
40166      * Selects a cell.
40167      * @param {Number} rowIndex
40168      * @param {Number} collIndex
40169      */
40170     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40171         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40172             this.clearSelections();
40173             r = r || this.grid.dataSource.getAt(rowIndex);
40174             this.selection = {
40175                 record : r,
40176                 cell : [rowIndex, colIndex]
40177             };
40178             if(!preventViewNotify){
40179                 var v = this.grid.getView();
40180                 v.onCellSelect(rowIndex, colIndex);
40181                 if(preventFocus !== true){
40182                     v.focusCell(rowIndex, colIndex);
40183                 }
40184             }
40185             this.fireEvent("cellselect", this, rowIndex, colIndex);
40186             this.fireEvent("selectionchange", this, this.selection);
40187         }
40188     },
40189
40190         //private
40191     isSelectable : function(rowIndex, colIndex, cm){
40192         return !cm.isHidden(colIndex);
40193     },
40194
40195     /** @ignore */
40196     handleKeyDown : function(e){
40197         //Roo.log('Cell Sel Model handleKeyDown');
40198         if(!e.isNavKeyPress()){
40199             return;
40200         }
40201         var g = this.grid, s = this.selection;
40202         if(!s){
40203             e.stopEvent();
40204             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40205             if(cell){
40206                 this.select(cell[0], cell[1]);
40207             }
40208             return;
40209         }
40210         var sm = this;
40211         var walk = function(row, col, step){
40212             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40213         };
40214         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40215         var newCell;
40216
40217       
40218
40219         switch(k){
40220             case e.TAB:
40221                 // handled by onEditorKey
40222                 if (g.isEditor && g.editing) {
40223                     return;
40224                 }
40225                 if(e.shiftKey) {
40226                     newCell = walk(r, c-1, -1);
40227                 } else {
40228                     newCell = walk(r, c+1, 1);
40229                 }
40230                 break;
40231             
40232             case e.DOWN:
40233                newCell = walk(r+1, c, 1);
40234                 break;
40235             
40236             case e.UP:
40237                 newCell = walk(r-1, c, -1);
40238                 break;
40239             
40240             case e.RIGHT:
40241                 newCell = walk(r, c+1, 1);
40242                 break;
40243             
40244             case e.LEFT:
40245                 newCell = walk(r, c-1, -1);
40246                 break;
40247             
40248             case e.ENTER:
40249                 
40250                 if(g.isEditor && !g.editing){
40251                    g.startEditing(r, c);
40252                    e.stopEvent();
40253                    return;
40254                 }
40255                 
40256                 
40257              break;
40258         };
40259         if(newCell){
40260             this.select(newCell[0], newCell[1]);
40261             e.stopEvent();
40262             
40263         }
40264     },
40265
40266     acceptsNav : function(row, col, cm){
40267         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40268     },
40269     /**
40270      * Selects a cell.
40271      * @param {Number} field (not used) - as it's normally used as a listener
40272      * @param {Number} e - event - fake it by using
40273      *
40274      * var e = Roo.EventObjectImpl.prototype;
40275      * e.keyCode = e.TAB
40276      *
40277      * 
40278      */
40279     onEditorKey : function(field, e){
40280         
40281         var k = e.getKey(),
40282             newCell,
40283             g = this.grid,
40284             ed = g.activeEditor,
40285             forward = false;
40286         ///Roo.log('onEditorKey' + k);
40287         
40288         
40289         if (this.enter_is_tab && k == e.ENTER) {
40290             k = e.TAB;
40291         }
40292         
40293         if(k == e.TAB){
40294             if(e.shiftKey){
40295                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40296             }else{
40297                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40298                 forward = true;
40299             }
40300             
40301             e.stopEvent();
40302             
40303         } else if(k == e.ENTER &&  !e.ctrlKey){
40304             ed.completeEdit();
40305             e.stopEvent();
40306             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40307         
40308                 } else if(k == e.ESC){
40309             ed.cancelEdit();
40310         }
40311                 
40312         if (newCell) {
40313             var ecall = { cell : newCell, forward : forward };
40314             this.fireEvent('beforeeditnext', ecall );
40315             newCell = ecall.cell;
40316                         forward = ecall.forward;
40317         }
40318                 
40319         if(newCell){
40320             //Roo.log('next cell after edit');
40321             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40322         } else if (forward) {
40323             // tabbed past last
40324             this.fireEvent.defer(100, this, ['tabend',this]);
40325         }
40326     }
40327 });/*
40328  * Based on:
40329  * Ext JS Library 1.1.1
40330  * Copyright(c) 2006-2007, Ext JS, LLC.
40331  *
40332  * Originally Released Under LGPL - original licence link has changed is not relivant.
40333  *
40334  * Fork - LGPL
40335  * <script type="text/javascript">
40336  */
40337  
40338 /**
40339  * @class Roo.grid.EditorGrid
40340  * @extends Roo.grid.Grid
40341  * Class for creating and editable grid.
40342  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40343  * The container MUST have some type of size defined for the grid to fill. The container will be 
40344  * automatically set to position relative if it isn't already.
40345  * @param {Object} dataSource The data model to bind to
40346  * @param {Object} colModel The column model with info about this grid's columns
40347  */
40348 Roo.grid.EditorGrid = function(container, config){
40349     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40350     this.getGridEl().addClass("xedit-grid");
40351
40352     if(!this.selModel){
40353         this.selModel = new Roo.grid.CellSelectionModel();
40354     }
40355
40356     this.activeEditor = null;
40357
40358         this.addEvents({
40359             /**
40360              * @event beforeedit
40361              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40362              * <ul style="padding:5px;padding-left:16px;">
40363              * <li>grid - This grid</li>
40364              * <li>record - The record being edited</li>
40365              * <li>field - The field name being edited</li>
40366              * <li>value - The value for the field being edited.</li>
40367              * <li>row - The grid row index</li>
40368              * <li>column - The grid column index</li>
40369              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40370              * </ul>
40371              * @param {Object} e An edit event (see above for description)
40372              */
40373             "beforeedit" : true,
40374             /**
40375              * @event afteredit
40376              * Fires after a cell is edited. <br />
40377              * <ul style="padding:5px;padding-left:16px;">
40378              * <li>grid - This grid</li>
40379              * <li>record - The record being edited</li>
40380              * <li>field - The field name being edited</li>
40381              * <li>value - The value being set</li>
40382              * <li>originalValue - The original value for the field, before the edit.</li>
40383              * <li>row - The grid row index</li>
40384              * <li>column - The grid column index</li>
40385              * </ul>
40386              * @param {Object} e An edit event (see above for description)
40387              */
40388             "afteredit" : true,
40389             /**
40390              * @event validateedit
40391              * Fires after a cell is edited, but before the value is set in the record. 
40392          * You can use this to modify the value being set in the field, Return false
40393              * to cancel the change. The edit event object has the following properties <br />
40394              * <ul style="padding:5px;padding-left:16px;">
40395          * <li>editor - This editor</li>
40396              * <li>grid - This grid</li>
40397              * <li>record - The record being edited</li>
40398              * <li>field - The field name being edited</li>
40399              * <li>value - The value being set</li>
40400              * <li>originalValue - The original value for the field, before the edit.</li>
40401              * <li>row - The grid row index</li>
40402              * <li>column - The grid column index</li>
40403              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40404              * </ul>
40405              * @param {Object} e An edit event (see above for description)
40406              */
40407             "validateedit" : true
40408         });
40409     this.on("bodyscroll", this.stopEditing,  this);
40410     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40411 };
40412
40413 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40414     /**
40415      * @cfg {Number} clicksToEdit
40416      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40417      */
40418     clicksToEdit: 2,
40419
40420     // private
40421     isEditor : true,
40422     // private
40423     trackMouseOver: false, // causes very odd FF errors
40424
40425     onCellDblClick : function(g, row, col){
40426         this.startEditing(row, col);
40427     },
40428
40429     onEditComplete : function(ed, value, startValue){
40430         this.editing = false;
40431         this.activeEditor = null;
40432         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40433         var r = ed.record;
40434         var field = this.colModel.getDataIndex(ed.col);
40435         var e = {
40436             grid: this,
40437             record: r,
40438             field: field,
40439             originalValue: startValue,
40440             value: value,
40441             row: ed.row,
40442             column: ed.col,
40443             cancel:false,
40444             editor: ed
40445         };
40446         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
40447         cell.show();
40448           
40449         if(String(value) !== String(startValue)){
40450             
40451             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40452                 r.set(field, e.value);
40453                 // if we are dealing with a combo box..
40454                 // then we also set the 'name' colum to be the displayField
40455                 if (ed.field.displayField && ed.field.name) {
40456                     r.set(ed.field.name, ed.field.el.dom.value);
40457                 }
40458                 
40459                 delete e.cancel; //?? why!!!
40460                 this.fireEvent("afteredit", e);
40461             }
40462         } else {
40463             this.fireEvent("afteredit", e); // always fire it!
40464         }
40465         this.view.focusCell(ed.row, ed.col);
40466     },
40467
40468     /**
40469      * Starts editing the specified for the specified row/column
40470      * @param {Number} rowIndex
40471      * @param {Number} colIndex
40472      */
40473     startEditing : function(row, col){
40474         this.stopEditing();
40475         if(this.colModel.isCellEditable(col, row)){
40476             this.view.ensureVisible(row, col, true);
40477           
40478             var r = this.dataSource.getAt(row);
40479             var field = this.colModel.getDataIndex(col);
40480             var cell = Roo.get(this.view.getCell(row,col));
40481             var e = {
40482                 grid: this,
40483                 record: r,
40484                 field: field,
40485                 value: r.data[field],
40486                 row: row,
40487                 column: col,
40488                 cancel:false 
40489             };
40490             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40491                 this.editing = true;
40492                 var ed = this.colModel.getCellEditor(col, row);
40493                 
40494                 if (!ed) {
40495                     return;
40496                 }
40497                 if(!ed.rendered){
40498                     ed.render(ed.parentEl || document.body);
40499                 }
40500                 ed.field.reset();
40501                
40502                 cell.hide();
40503                 
40504                 (function(){ // complex but required for focus issues in safari, ie and opera
40505                     ed.row = row;
40506                     ed.col = col;
40507                     ed.record = r;
40508                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40509                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40510                     this.activeEditor = ed;
40511                     var v = r.data[field];
40512                     ed.startEdit(this.view.getCell(row, col), v);
40513                     // combo's with 'displayField and name set
40514                     if (ed.field.displayField && ed.field.name) {
40515                         ed.field.el.dom.value = r.data[ed.field.name];
40516                     }
40517                     
40518                     
40519                 }).defer(50, this);
40520             }
40521         }
40522     },
40523         
40524     /**
40525      * Stops any active editing
40526      */
40527     stopEditing : function(){
40528         if(this.activeEditor){
40529             this.activeEditor.completeEdit();
40530         }
40531         this.activeEditor = null;
40532     },
40533         
40534          /**
40535      * Called to get grid's drag proxy text, by default returns this.ddText.
40536      * @return {String}
40537      */
40538     getDragDropText : function(){
40539         var count = this.selModel.getSelectedCell() ? 1 : 0;
40540         return String.format(this.ddText, count, count == 1 ? '' : 's');
40541     }
40542         
40543 });/*
40544  * Based on:
40545  * Ext JS Library 1.1.1
40546  * Copyright(c) 2006-2007, Ext JS, LLC.
40547  *
40548  * Originally Released Under LGPL - original licence link has changed is not relivant.
40549  *
40550  * Fork - LGPL
40551  * <script type="text/javascript">
40552  */
40553
40554 // private - not really -- you end up using it !
40555 // This is a support class used internally by the Grid components
40556
40557 /**
40558  * @class Roo.grid.GridEditor
40559  * @extends Roo.Editor
40560  * Class for creating and editable grid elements.
40561  * @param {Object} config any settings (must include field)
40562  */
40563 Roo.grid.GridEditor = function(field, config){
40564     if (!config && field.field) {
40565         config = field;
40566         field = Roo.factory(config.field, Roo.form);
40567     }
40568     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40569     field.monitorTab = false;
40570 };
40571
40572 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40573     
40574     /**
40575      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40576      */
40577     
40578     alignment: "tl-tl",
40579     autoSize: "width",
40580     hideEl : false,
40581     cls: "x-small-editor x-grid-editor",
40582     shim:false,
40583     shadow:"frame"
40584 });/*
40585  * Based on:
40586  * Ext JS Library 1.1.1
40587  * Copyright(c) 2006-2007, Ext JS, LLC.
40588  *
40589  * Originally Released Under LGPL - original licence link has changed is not relivant.
40590  *
40591  * Fork - LGPL
40592  * <script type="text/javascript">
40593  */
40594   
40595
40596   
40597 Roo.grid.PropertyRecord = Roo.data.Record.create([
40598     {name:'name',type:'string'},  'value'
40599 ]);
40600
40601
40602 Roo.grid.PropertyStore = function(grid, source){
40603     this.grid = grid;
40604     this.store = new Roo.data.Store({
40605         recordType : Roo.grid.PropertyRecord
40606     });
40607     this.store.on('update', this.onUpdate,  this);
40608     if(source){
40609         this.setSource(source);
40610     }
40611     Roo.grid.PropertyStore.superclass.constructor.call(this);
40612 };
40613
40614
40615
40616 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40617     setSource : function(o){
40618         this.source = o;
40619         this.store.removeAll();
40620         var data = [];
40621         for(var k in o){
40622             if(this.isEditableValue(o[k])){
40623                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40624             }
40625         }
40626         this.store.loadRecords({records: data}, {}, true);
40627     },
40628
40629     onUpdate : function(ds, record, type){
40630         if(type == Roo.data.Record.EDIT){
40631             var v = record.data['value'];
40632             var oldValue = record.modified['value'];
40633             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40634                 this.source[record.id] = v;
40635                 record.commit();
40636                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40637             }else{
40638                 record.reject();
40639             }
40640         }
40641     },
40642
40643     getProperty : function(row){
40644        return this.store.getAt(row);
40645     },
40646
40647     isEditableValue: function(val){
40648         if(val && val instanceof Date){
40649             return true;
40650         }else if(typeof val == 'object' || typeof val == 'function'){
40651             return false;
40652         }
40653         return true;
40654     },
40655
40656     setValue : function(prop, value){
40657         this.source[prop] = value;
40658         this.store.getById(prop).set('value', value);
40659     },
40660
40661     getSource : function(){
40662         return this.source;
40663     }
40664 });
40665
40666 Roo.grid.PropertyColumnModel = function(grid, store){
40667     this.grid = grid;
40668     var g = Roo.grid;
40669     g.PropertyColumnModel.superclass.constructor.call(this, [
40670         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40671         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40672     ]);
40673     this.store = store;
40674     this.bselect = Roo.DomHelper.append(document.body, {
40675         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40676             {tag: 'option', value: 'true', html: 'true'},
40677             {tag: 'option', value: 'false', html: 'false'}
40678         ]
40679     });
40680     Roo.id(this.bselect);
40681     var f = Roo.form;
40682     this.editors = {
40683         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40684         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40685         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40686         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40687         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40688     };
40689     this.renderCellDelegate = this.renderCell.createDelegate(this);
40690     this.renderPropDelegate = this.renderProp.createDelegate(this);
40691 };
40692
40693 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40694     
40695     
40696     nameText : 'Name',
40697     valueText : 'Value',
40698     
40699     dateFormat : 'm/j/Y',
40700     
40701     
40702     renderDate : function(dateVal){
40703         return dateVal.dateFormat(this.dateFormat);
40704     },
40705
40706     renderBool : function(bVal){
40707         return bVal ? 'true' : 'false';
40708     },
40709
40710     isCellEditable : function(colIndex, rowIndex){
40711         return colIndex == 1;
40712     },
40713
40714     getRenderer : function(col){
40715         return col == 1 ?
40716             this.renderCellDelegate : this.renderPropDelegate;
40717     },
40718
40719     renderProp : function(v){
40720         return this.getPropertyName(v);
40721     },
40722
40723     renderCell : function(val){
40724         var rv = val;
40725         if(val instanceof Date){
40726             rv = this.renderDate(val);
40727         }else if(typeof val == 'boolean'){
40728             rv = this.renderBool(val);
40729         }
40730         return Roo.util.Format.htmlEncode(rv);
40731     },
40732
40733     getPropertyName : function(name){
40734         var pn = this.grid.propertyNames;
40735         return pn && pn[name] ? pn[name] : name;
40736     },
40737
40738     getCellEditor : function(colIndex, rowIndex){
40739         var p = this.store.getProperty(rowIndex);
40740         var n = p.data['name'], val = p.data['value'];
40741         
40742         if(typeof(this.grid.customEditors[n]) == 'string'){
40743             return this.editors[this.grid.customEditors[n]];
40744         }
40745         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40746             return this.grid.customEditors[n];
40747         }
40748         if(val instanceof Date){
40749             return this.editors['date'];
40750         }else if(typeof val == 'number'){
40751             return this.editors['number'];
40752         }else if(typeof val == 'boolean'){
40753             return this.editors['boolean'];
40754         }else{
40755             return this.editors['string'];
40756         }
40757     }
40758 });
40759
40760 /**
40761  * @class Roo.grid.PropertyGrid
40762  * @extends Roo.grid.EditorGrid
40763  * This class represents the  interface of a component based property grid control.
40764  * <br><br>Usage:<pre><code>
40765  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40766       
40767  });
40768  // set any options
40769  grid.render();
40770  * </code></pre>
40771   
40772  * @constructor
40773  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40774  * The container MUST have some type of size defined for the grid to fill. The container will be
40775  * automatically set to position relative if it isn't already.
40776  * @param {Object} config A config object that sets properties on this grid.
40777  */
40778 Roo.grid.PropertyGrid = function(container, config){
40779     config = config || {};
40780     var store = new Roo.grid.PropertyStore(this);
40781     this.store = store;
40782     var cm = new Roo.grid.PropertyColumnModel(this, store);
40783     store.store.sort('name', 'ASC');
40784     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40785         ds: store.store,
40786         cm: cm,
40787         enableColLock:false,
40788         enableColumnMove:false,
40789         stripeRows:false,
40790         trackMouseOver: false,
40791         clicksToEdit:1
40792     }, config));
40793     this.getGridEl().addClass('x-props-grid');
40794     this.lastEditRow = null;
40795     this.on('columnresize', this.onColumnResize, this);
40796     this.addEvents({
40797          /**
40798              * @event beforepropertychange
40799              * Fires before a property changes (return false to stop?)
40800              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40801              * @param {String} id Record Id
40802              * @param {String} newval New Value
40803          * @param {String} oldval Old Value
40804              */
40805         "beforepropertychange": true,
40806         /**
40807              * @event propertychange
40808              * Fires after a property changes
40809              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40810              * @param {String} id Record Id
40811              * @param {String} newval New Value
40812          * @param {String} oldval Old Value
40813              */
40814         "propertychange": true
40815     });
40816     this.customEditors = this.customEditors || {};
40817 };
40818 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40819     
40820      /**
40821      * @cfg {Object} customEditors map of colnames=> custom editors.
40822      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40823      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40824      * false disables editing of the field.
40825          */
40826     
40827       /**
40828      * @cfg {Object} propertyNames map of property Names to their displayed value
40829          */
40830     
40831     render : function(){
40832         Roo.grid.PropertyGrid.superclass.render.call(this);
40833         this.autoSize.defer(100, this);
40834     },
40835
40836     autoSize : function(){
40837         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40838         if(this.view){
40839             this.view.fitColumns();
40840         }
40841     },
40842
40843     onColumnResize : function(){
40844         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40845         this.autoSize();
40846     },
40847     /**
40848      * Sets the data for the Grid
40849      * accepts a Key => Value object of all the elements avaiable.
40850      * @param {Object} data  to appear in grid.
40851      */
40852     setSource : function(source){
40853         this.store.setSource(source);
40854         //this.autoSize();
40855     },
40856     /**
40857      * Gets all the data from the grid.
40858      * @return {Object} data  data stored in grid
40859      */
40860     getSource : function(){
40861         return this.store.getSource();
40862     }
40863 });/*
40864   
40865  * Licence LGPL
40866  
40867  */
40868  
40869 /**
40870  * @class Roo.grid.Calendar
40871  * @extends Roo.util.Grid
40872  * This class extends the Grid to provide a calendar widget
40873  * <br><br>Usage:<pre><code>
40874  var grid = new Roo.grid.Calendar("my-container-id", {
40875      ds: myDataStore,
40876      cm: myColModel,
40877      selModel: mySelectionModel,
40878      autoSizeColumns: true,
40879      monitorWindowResize: false,
40880      trackMouseOver: true
40881      eventstore : real data store..
40882  });
40883  // set any options
40884  grid.render();
40885   
40886   * @constructor
40887  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40888  * The container MUST have some type of size defined for the grid to fill. The container will be
40889  * automatically set to position relative if it isn't already.
40890  * @param {Object} config A config object that sets properties on this grid.
40891  */
40892 Roo.grid.Calendar = function(container, config){
40893         // initialize the container
40894         this.container = Roo.get(container);
40895         this.container.update("");
40896         this.container.setStyle("overflow", "hidden");
40897     this.container.addClass('x-grid-container');
40898
40899     this.id = this.container.id;
40900
40901     Roo.apply(this, config);
40902     // check and correct shorthanded configs
40903     
40904     var rows = [];
40905     var d =1;
40906     for (var r = 0;r < 6;r++) {
40907         
40908         rows[r]=[];
40909         for (var c =0;c < 7;c++) {
40910             rows[r][c]= '';
40911         }
40912     }
40913     if (this.eventStore) {
40914         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40915         this.eventStore.on('load',this.onLoad, this);
40916         this.eventStore.on('beforeload',this.clearEvents, this);
40917          
40918     }
40919     
40920     this.dataSource = new Roo.data.Store({
40921             proxy: new Roo.data.MemoryProxy(rows),
40922             reader: new Roo.data.ArrayReader({}, [
40923                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40924     });
40925
40926     this.dataSource.load();
40927     this.ds = this.dataSource;
40928     this.ds.xmodule = this.xmodule || false;
40929     
40930     
40931     var cellRender = function(v,x,r)
40932     {
40933         return String.format(
40934             '<div class="fc-day  fc-widget-content"><div>' +
40935                 '<div class="fc-event-container"></div>' +
40936                 '<div class="fc-day-number">{0}</div>'+
40937                 
40938                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40939             '</div></div>', v);
40940     
40941     }
40942     
40943     
40944     this.colModel = new Roo.grid.ColumnModel( [
40945         {
40946             xtype: 'ColumnModel',
40947             xns: Roo.grid,
40948             dataIndex : 'weekday0',
40949             header : 'Sunday',
40950             renderer : cellRender
40951         },
40952         {
40953             xtype: 'ColumnModel',
40954             xns: Roo.grid,
40955             dataIndex : 'weekday1',
40956             header : 'Monday',
40957             renderer : cellRender
40958         },
40959         {
40960             xtype: 'ColumnModel',
40961             xns: Roo.grid,
40962             dataIndex : 'weekday2',
40963             header : 'Tuesday',
40964             renderer : cellRender
40965         },
40966         {
40967             xtype: 'ColumnModel',
40968             xns: Roo.grid,
40969             dataIndex : 'weekday3',
40970             header : 'Wednesday',
40971             renderer : cellRender
40972         },
40973         {
40974             xtype: 'ColumnModel',
40975             xns: Roo.grid,
40976             dataIndex : 'weekday4',
40977             header : 'Thursday',
40978             renderer : cellRender
40979         },
40980         {
40981             xtype: 'ColumnModel',
40982             xns: Roo.grid,
40983             dataIndex : 'weekday5',
40984             header : 'Friday',
40985             renderer : cellRender
40986         },
40987         {
40988             xtype: 'ColumnModel',
40989             xns: Roo.grid,
40990             dataIndex : 'weekday6',
40991             header : 'Saturday',
40992             renderer : cellRender
40993         }
40994     ]);
40995     this.cm = this.colModel;
40996     this.cm.xmodule = this.xmodule || false;
40997  
40998         
40999           
41000     //this.selModel = new Roo.grid.CellSelectionModel();
41001     //this.sm = this.selModel;
41002     //this.selModel.init(this);
41003     
41004     
41005     if(this.width){
41006         this.container.setWidth(this.width);
41007     }
41008
41009     if(this.height){
41010         this.container.setHeight(this.height);
41011     }
41012     /** @private */
41013         this.addEvents({
41014         // raw events
41015         /**
41016          * @event click
41017          * The raw click event for the entire grid.
41018          * @param {Roo.EventObject} e
41019          */
41020         "click" : true,
41021         /**
41022          * @event dblclick
41023          * The raw dblclick event for the entire grid.
41024          * @param {Roo.EventObject} e
41025          */
41026         "dblclick" : true,
41027         /**
41028          * @event contextmenu
41029          * The raw contextmenu event for the entire grid.
41030          * @param {Roo.EventObject} e
41031          */
41032         "contextmenu" : true,
41033         /**
41034          * @event mousedown
41035          * The raw mousedown event for the entire grid.
41036          * @param {Roo.EventObject} e
41037          */
41038         "mousedown" : true,
41039         /**
41040          * @event mouseup
41041          * The raw mouseup event for the entire grid.
41042          * @param {Roo.EventObject} e
41043          */
41044         "mouseup" : true,
41045         /**
41046          * @event mouseover
41047          * The raw mouseover event for the entire grid.
41048          * @param {Roo.EventObject} e
41049          */
41050         "mouseover" : true,
41051         /**
41052          * @event mouseout
41053          * The raw mouseout event for the entire grid.
41054          * @param {Roo.EventObject} e
41055          */
41056         "mouseout" : true,
41057         /**
41058          * @event keypress
41059          * The raw keypress event for the entire grid.
41060          * @param {Roo.EventObject} e
41061          */
41062         "keypress" : true,
41063         /**
41064          * @event keydown
41065          * The raw keydown event for the entire grid.
41066          * @param {Roo.EventObject} e
41067          */
41068         "keydown" : true,
41069
41070         // custom events
41071
41072         /**
41073          * @event cellclick
41074          * Fires when a cell is clicked
41075          * @param {Grid} this
41076          * @param {Number} rowIndex
41077          * @param {Number} columnIndex
41078          * @param {Roo.EventObject} e
41079          */
41080         "cellclick" : true,
41081         /**
41082          * @event celldblclick
41083          * Fires when a cell is double clicked
41084          * @param {Grid} this
41085          * @param {Number} rowIndex
41086          * @param {Number} columnIndex
41087          * @param {Roo.EventObject} e
41088          */
41089         "celldblclick" : true,
41090         /**
41091          * @event rowclick
41092          * Fires when a row is clicked
41093          * @param {Grid} this
41094          * @param {Number} rowIndex
41095          * @param {Roo.EventObject} e
41096          */
41097         "rowclick" : true,
41098         /**
41099          * @event rowdblclick
41100          * Fires when a row is double clicked
41101          * @param {Grid} this
41102          * @param {Number} rowIndex
41103          * @param {Roo.EventObject} e
41104          */
41105         "rowdblclick" : true,
41106         /**
41107          * @event headerclick
41108          * Fires when a header is clicked
41109          * @param {Grid} this
41110          * @param {Number} columnIndex
41111          * @param {Roo.EventObject} e
41112          */
41113         "headerclick" : true,
41114         /**
41115          * @event headerdblclick
41116          * Fires when a header cell is double clicked
41117          * @param {Grid} this
41118          * @param {Number} columnIndex
41119          * @param {Roo.EventObject} e
41120          */
41121         "headerdblclick" : true,
41122         /**
41123          * @event rowcontextmenu
41124          * Fires when a row is right clicked
41125          * @param {Grid} this
41126          * @param {Number} rowIndex
41127          * @param {Roo.EventObject} e
41128          */
41129         "rowcontextmenu" : true,
41130         /**
41131          * @event cellcontextmenu
41132          * Fires when a cell is right clicked
41133          * @param {Grid} this
41134          * @param {Number} rowIndex
41135          * @param {Number} cellIndex
41136          * @param {Roo.EventObject} e
41137          */
41138          "cellcontextmenu" : true,
41139         /**
41140          * @event headercontextmenu
41141          * Fires when a header is right clicked
41142          * @param {Grid} this
41143          * @param {Number} columnIndex
41144          * @param {Roo.EventObject} e
41145          */
41146         "headercontextmenu" : true,
41147         /**
41148          * @event bodyscroll
41149          * Fires when the body element is scrolled
41150          * @param {Number} scrollLeft
41151          * @param {Number} scrollTop
41152          */
41153         "bodyscroll" : true,
41154         /**
41155          * @event columnresize
41156          * Fires when the user resizes a column
41157          * @param {Number} columnIndex
41158          * @param {Number} newSize
41159          */
41160         "columnresize" : true,
41161         /**
41162          * @event columnmove
41163          * Fires when the user moves a column
41164          * @param {Number} oldIndex
41165          * @param {Number} newIndex
41166          */
41167         "columnmove" : true,
41168         /**
41169          * @event startdrag
41170          * Fires when row(s) start being dragged
41171          * @param {Grid} this
41172          * @param {Roo.GridDD} dd The drag drop object
41173          * @param {event} e The raw browser event
41174          */
41175         "startdrag" : true,
41176         /**
41177          * @event enddrag
41178          * Fires when a drag operation is complete
41179          * @param {Grid} this
41180          * @param {Roo.GridDD} dd The drag drop object
41181          * @param {event} e The raw browser event
41182          */
41183         "enddrag" : true,
41184         /**
41185          * @event dragdrop
41186          * Fires when dragged row(s) are dropped on a valid DD target
41187          * @param {Grid} this
41188          * @param {Roo.GridDD} dd The drag drop object
41189          * @param {String} targetId The target drag drop object
41190          * @param {event} e The raw browser event
41191          */
41192         "dragdrop" : true,
41193         /**
41194          * @event dragover
41195          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41196          * @param {Grid} this
41197          * @param {Roo.GridDD} dd The drag drop object
41198          * @param {String} targetId The target drag drop object
41199          * @param {event} e The raw browser event
41200          */
41201         "dragover" : true,
41202         /**
41203          * @event dragenter
41204          *  Fires when the dragged row(s) first cross another DD target while being dragged
41205          * @param {Grid} this
41206          * @param {Roo.GridDD} dd The drag drop object
41207          * @param {String} targetId The target drag drop object
41208          * @param {event} e The raw browser event
41209          */
41210         "dragenter" : true,
41211         /**
41212          * @event dragout
41213          * Fires when the dragged row(s) leave another DD target while being dragged
41214          * @param {Grid} this
41215          * @param {Roo.GridDD} dd The drag drop object
41216          * @param {String} targetId The target drag drop object
41217          * @param {event} e The raw browser event
41218          */
41219         "dragout" : true,
41220         /**
41221          * @event rowclass
41222          * Fires when a row is rendered, so you can change add a style to it.
41223          * @param {GridView} gridview   The grid view
41224          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41225          */
41226         'rowclass' : true,
41227
41228         /**
41229          * @event render
41230          * Fires when the grid is rendered
41231          * @param {Grid} grid
41232          */
41233         'render' : true,
41234             /**
41235              * @event select
41236              * Fires when a date is selected
41237              * @param {DatePicker} this
41238              * @param {Date} date The selected date
41239              */
41240         'select': true,
41241         /**
41242              * @event monthchange
41243              * Fires when the displayed month changes 
41244              * @param {DatePicker} this
41245              * @param {Date} date The selected month
41246              */
41247         'monthchange': true,
41248         /**
41249              * @event evententer
41250              * Fires when mouse over an event
41251              * @param {Calendar} this
41252              * @param {event} Event
41253              */
41254         'evententer': true,
41255         /**
41256              * @event eventleave
41257              * Fires when the mouse leaves an
41258              * @param {Calendar} this
41259              * @param {event}
41260              */
41261         'eventleave': true,
41262         /**
41263              * @event eventclick
41264              * Fires when the mouse click an
41265              * @param {Calendar} this
41266              * @param {event}
41267              */
41268         'eventclick': true,
41269         /**
41270              * @event eventrender
41271              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41272              * @param {Calendar} this
41273              * @param {data} data to be modified
41274              */
41275         'eventrender': true
41276         
41277     });
41278
41279     Roo.grid.Grid.superclass.constructor.call(this);
41280     this.on('render', function() {
41281         this.view.el.addClass('x-grid-cal'); 
41282         
41283         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41284
41285     },this);
41286     
41287     if (!Roo.grid.Calendar.style) {
41288         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41289             
41290             
41291             '.x-grid-cal .x-grid-col' :  {
41292                 height: 'auto !important',
41293                 'vertical-align': 'top'
41294             },
41295             '.x-grid-cal  .fc-event-hori' : {
41296                 height: '14px'
41297             }
41298              
41299             
41300         }, Roo.id());
41301     }
41302
41303     
41304     
41305 };
41306 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41307     /**
41308      * @cfg {Store} eventStore The store that loads events.
41309      */
41310     eventStore : 25,
41311
41312      
41313     activeDate : false,
41314     startDay : 0,
41315     autoWidth : true,
41316     monitorWindowResize : false,
41317
41318     
41319     resizeColumns : function() {
41320         var col = (this.view.el.getWidth() / 7) - 3;
41321         // loop through cols, and setWidth
41322         for(var i =0 ; i < 7 ; i++){
41323             this.cm.setColumnWidth(i, col);
41324         }
41325     },
41326      setDate :function(date) {
41327         
41328         Roo.log('setDate?');
41329         
41330         this.resizeColumns();
41331         var vd = this.activeDate;
41332         this.activeDate = date;
41333 //        if(vd && this.el){
41334 //            var t = date.getTime();
41335 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41336 //                Roo.log('using add remove');
41337 //                
41338 //                this.fireEvent('monthchange', this, date);
41339 //                
41340 //                this.cells.removeClass("fc-state-highlight");
41341 //                this.cells.each(function(c){
41342 //                   if(c.dateValue == t){
41343 //                       c.addClass("fc-state-highlight");
41344 //                       setTimeout(function(){
41345 //                            try{c.dom.firstChild.focus();}catch(e){}
41346 //                       }, 50);
41347 //                       return false;
41348 //                   }
41349 //                   return true;
41350 //                });
41351 //                return;
41352 //            }
41353 //        }
41354         
41355         var days = date.getDaysInMonth();
41356         
41357         var firstOfMonth = date.getFirstDateOfMonth();
41358         var startingPos = firstOfMonth.getDay()-this.startDay;
41359         
41360         if(startingPos < this.startDay){
41361             startingPos += 7;
41362         }
41363         
41364         var pm = date.add(Date.MONTH, -1);
41365         var prevStart = pm.getDaysInMonth()-startingPos;
41366 //        
41367         
41368         
41369         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41370         
41371         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41372         //this.cells.addClassOnOver('fc-state-hover');
41373         
41374         var cells = this.cells.elements;
41375         var textEls = this.textNodes;
41376         
41377         //Roo.each(cells, function(cell){
41378         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41379         //});
41380         
41381         days += startingPos;
41382
41383         // convert everything to numbers so it's fast
41384         var day = 86400000;
41385         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41386         //Roo.log(d);
41387         //Roo.log(pm);
41388         //Roo.log(prevStart);
41389         
41390         var today = new Date().clearTime().getTime();
41391         var sel = date.clearTime().getTime();
41392         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41393         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41394         var ddMatch = this.disabledDatesRE;
41395         var ddText = this.disabledDatesText;
41396         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41397         var ddaysText = this.disabledDaysText;
41398         var format = this.format;
41399         
41400         var setCellClass = function(cal, cell){
41401             
41402             //Roo.log('set Cell Class');
41403             cell.title = "";
41404             var t = d.getTime();
41405             
41406             //Roo.log(d);
41407             
41408             
41409             cell.dateValue = t;
41410             if(t == today){
41411                 cell.className += " fc-today";
41412                 cell.className += " fc-state-highlight";
41413                 cell.title = cal.todayText;
41414             }
41415             if(t == sel){
41416                 // disable highlight in other month..
41417                 cell.className += " fc-state-highlight";
41418                 
41419             }
41420             // disabling
41421             if(t < min) {
41422                 //cell.className = " fc-state-disabled";
41423                 cell.title = cal.minText;
41424                 return;
41425             }
41426             if(t > max) {
41427                 //cell.className = " fc-state-disabled";
41428                 cell.title = cal.maxText;
41429                 return;
41430             }
41431             if(ddays){
41432                 if(ddays.indexOf(d.getDay()) != -1){
41433                     // cell.title = ddaysText;
41434                    // cell.className = " fc-state-disabled";
41435                 }
41436             }
41437             if(ddMatch && format){
41438                 var fvalue = d.dateFormat(format);
41439                 if(ddMatch.test(fvalue)){
41440                     cell.title = ddText.replace("%0", fvalue);
41441                    cell.className = " fc-state-disabled";
41442                 }
41443             }
41444             
41445             if (!cell.initialClassName) {
41446                 cell.initialClassName = cell.dom.className;
41447             }
41448             
41449             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41450         };
41451
41452         var i = 0;
41453         
41454         for(; i < startingPos; i++) {
41455             cells[i].dayName =  (++prevStart);
41456             Roo.log(textEls[i]);
41457             d.setDate(d.getDate()+1);
41458             
41459             //cells[i].className = "fc-past fc-other-month";
41460             setCellClass(this, cells[i]);
41461         }
41462         
41463         var intDay = 0;
41464         
41465         for(; i < days; i++){
41466             intDay = i - startingPos + 1;
41467             cells[i].dayName =  (intDay);
41468             d.setDate(d.getDate()+1);
41469             
41470             cells[i].className = ''; // "x-date-active";
41471             setCellClass(this, cells[i]);
41472         }
41473         var extraDays = 0;
41474         
41475         for(; i < 42; i++) {
41476             //textEls[i].innerHTML = (++extraDays);
41477             
41478             d.setDate(d.getDate()+1);
41479             cells[i].dayName = (++extraDays);
41480             cells[i].className = "fc-future fc-other-month";
41481             setCellClass(this, cells[i]);
41482         }
41483         
41484         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41485         
41486         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41487         
41488         // this will cause all the cells to mis
41489         var rows= [];
41490         var i =0;
41491         for (var r = 0;r < 6;r++) {
41492             for (var c =0;c < 7;c++) {
41493                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41494             }    
41495         }
41496         
41497         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41498         for(i=0;i<cells.length;i++) {
41499             
41500             this.cells.elements[i].dayName = cells[i].dayName ;
41501             this.cells.elements[i].className = cells[i].className;
41502             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41503             this.cells.elements[i].title = cells[i].title ;
41504             this.cells.elements[i].dateValue = cells[i].dateValue ;
41505         }
41506         
41507         
41508         
41509         
41510         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41511         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41512         
41513         ////if(totalRows != 6){
41514             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41515            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41516        // }
41517         
41518         this.fireEvent('monthchange', this, date);
41519         
41520         
41521     },
41522  /**
41523      * Returns the grid's SelectionModel.
41524      * @return {SelectionModel}
41525      */
41526     getSelectionModel : function(){
41527         if(!this.selModel){
41528             this.selModel = new Roo.grid.CellSelectionModel();
41529         }
41530         return this.selModel;
41531     },
41532
41533     load: function() {
41534         this.eventStore.load()
41535         
41536         
41537         
41538     },
41539     
41540     findCell : function(dt) {
41541         dt = dt.clearTime().getTime();
41542         var ret = false;
41543         this.cells.each(function(c){
41544             //Roo.log("check " +c.dateValue + '?=' + dt);
41545             if(c.dateValue == dt){
41546                 ret = c;
41547                 return false;
41548             }
41549             return true;
41550         });
41551         
41552         return ret;
41553     },
41554     
41555     findCells : function(rec) {
41556         var s = rec.data.start_dt.clone().clearTime().getTime();
41557        // Roo.log(s);
41558         var e= rec.data.end_dt.clone().clearTime().getTime();
41559        // Roo.log(e);
41560         var ret = [];
41561         this.cells.each(function(c){
41562              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41563             
41564             if(c.dateValue > e){
41565                 return ;
41566             }
41567             if(c.dateValue < s){
41568                 return ;
41569             }
41570             ret.push(c);
41571         });
41572         
41573         return ret;    
41574     },
41575     
41576     findBestRow: function(cells)
41577     {
41578         var ret = 0;
41579         
41580         for (var i =0 ; i < cells.length;i++) {
41581             ret  = Math.max(cells[i].rows || 0,ret);
41582         }
41583         return ret;
41584         
41585     },
41586     
41587     
41588     addItem : function(rec)
41589     {
41590         // look for vertical location slot in
41591         var cells = this.findCells(rec);
41592         
41593         rec.row = this.findBestRow(cells);
41594         
41595         // work out the location.
41596         
41597         var crow = false;
41598         var rows = [];
41599         for(var i =0; i < cells.length; i++) {
41600             if (!crow) {
41601                 crow = {
41602                     start : cells[i],
41603                     end :  cells[i]
41604                 };
41605                 continue;
41606             }
41607             if (crow.start.getY() == cells[i].getY()) {
41608                 // on same row.
41609                 crow.end = cells[i];
41610                 continue;
41611             }
41612             // different row.
41613             rows.push(crow);
41614             crow = {
41615                 start: cells[i],
41616                 end : cells[i]
41617             };
41618             
41619         }
41620         
41621         rows.push(crow);
41622         rec.els = [];
41623         rec.rows = rows;
41624         rec.cells = cells;
41625         for (var i = 0; i < cells.length;i++) {
41626             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41627             
41628         }
41629         
41630         
41631     },
41632     
41633     clearEvents: function() {
41634         
41635         if (!this.eventStore.getCount()) {
41636             return;
41637         }
41638         // reset number of rows in cells.
41639         Roo.each(this.cells.elements, function(c){
41640             c.rows = 0;
41641         });
41642         
41643         this.eventStore.each(function(e) {
41644             this.clearEvent(e);
41645         },this);
41646         
41647     },
41648     
41649     clearEvent : function(ev)
41650     {
41651         if (ev.els) {
41652             Roo.each(ev.els, function(el) {
41653                 el.un('mouseenter' ,this.onEventEnter, this);
41654                 el.un('mouseleave' ,this.onEventLeave, this);
41655                 el.remove();
41656             },this);
41657             ev.els = [];
41658         }
41659     },
41660     
41661     
41662     renderEvent : function(ev,ctr) {
41663         if (!ctr) {
41664              ctr = this.view.el.select('.fc-event-container',true).first();
41665         }
41666         
41667          
41668         this.clearEvent(ev);
41669             //code
41670        
41671         
41672         
41673         ev.els = [];
41674         var cells = ev.cells;
41675         var rows = ev.rows;
41676         this.fireEvent('eventrender', this, ev);
41677         
41678         for(var i =0; i < rows.length; i++) {
41679             
41680             cls = '';
41681             if (i == 0) {
41682                 cls += ' fc-event-start';
41683             }
41684             if ((i+1) == rows.length) {
41685                 cls += ' fc-event-end';
41686             }
41687             
41688             //Roo.log(ev.data);
41689             // how many rows should it span..
41690             var cg = this.eventTmpl.append(ctr,Roo.apply({
41691                 fccls : cls
41692                 
41693             }, ev.data) , true);
41694             
41695             
41696             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41697             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41698             cg.on('click', this.onEventClick, this, ev);
41699             
41700             ev.els.push(cg);
41701             
41702             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41703             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41704             //Roo.log(cg);
41705              
41706             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41707             cg.setWidth(ebox.right - sbox.x -2);
41708         }
41709     },
41710     
41711     renderEvents: function()
41712     {   
41713         // first make sure there is enough space..
41714         
41715         if (!this.eventTmpl) {
41716             this.eventTmpl = new Roo.Template(
41717                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41718                     '<div class="fc-event-inner">' +
41719                         '<span class="fc-event-time">{time}</span>' +
41720                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41721                     '</div>' +
41722                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41723                 '</div>'
41724             );
41725                 
41726         }
41727                
41728         
41729         
41730         this.cells.each(function(c) {
41731             //Roo.log(c.select('.fc-day-content div',true).first());
41732             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41733         });
41734         
41735         var ctr = this.view.el.select('.fc-event-container',true).first();
41736         
41737         var cls;
41738         this.eventStore.each(function(ev){
41739             
41740             this.renderEvent(ev);
41741              
41742              
41743         }, this);
41744         this.view.layout();
41745         
41746     },
41747     
41748     onEventEnter: function (e, el,event,d) {
41749         this.fireEvent('evententer', this, el, event);
41750     },
41751     
41752     onEventLeave: function (e, el,event,d) {
41753         this.fireEvent('eventleave', this, el, event);
41754     },
41755     
41756     onEventClick: function (e, el,event,d) {
41757         this.fireEvent('eventclick', this, el, event);
41758     },
41759     
41760     onMonthChange: function () {
41761         this.store.load();
41762     },
41763     
41764     onLoad: function () {
41765         
41766         //Roo.log('calendar onload');
41767 //         
41768         if(this.eventStore.getCount() > 0){
41769             
41770            
41771             
41772             this.eventStore.each(function(d){
41773                 
41774                 
41775                 // FIXME..
41776                 var add =   d.data;
41777                 if (typeof(add.end_dt) == 'undefined')  {
41778                     Roo.log("Missing End time in calendar data: ");
41779                     Roo.log(d);
41780                     return;
41781                 }
41782                 if (typeof(add.start_dt) == 'undefined')  {
41783                     Roo.log("Missing Start time in calendar data: ");
41784                     Roo.log(d);
41785                     return;
41786                 }
41787                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41788                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41789                 add.id = add.id || d.id;
41790                 add.title = add.title || '??';
41791                 
41792                 this.addItem(d);
41793                 
41794              
41795             },this);
41796         }
41797         
41798         this.renderEvents();
41799     }
41800     
41801
41802 });
41803 /*
41804  grid : {
41805                 xtype: 'Grid',
41806                 xns: Roo.grid,
41807                 listeners : {
41808                     render : function ()
41809                     {
41810                         _this.grid = this;
41811                         
41812                         if (!this.view.el.hasClass('course-timesheet')) {
41813                             this.view.el.addClass('course-timesheet');
41814                         }
41815                         if (this.tsStyle) {
41816                             this.ds.load({});
41817                             return; 
41818                         }
41819                         Roo.log('width');
41820                         Roo.log(_this.grid.view.el.getWidth());
41821                         
41822                         
41823                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41824                             '.course-timesheet .x-grid-row' : {
41825                                 height: '80px'
41826                             },
41827                             '.x-grid-row td' : {
41828                                 'vertical-align' : 0
41829                             },
41830                             '.course-edit-link' : {
41831                                 'color' : 'blue',
41832                                 'text-overflow' : 'ellipsis',
41833                                 'overflow' : 'hidden',
41834                                 'white-space' : 'nowrap',
41835                                 'cursor' : 'pointer'
41836                             },
41837                             '.sub-link' : {
41838                                 'color' : 'green'
41839                             },
41840                             '.de-act-sup-link' : {
41841                                 'color' : 'purple',
41842                                 'text-decoration' : 'line-through'
41843                             },
41844                             '.de-act-link' : {
41845                                 'color' : 'red',
41846                                 'text-decoration' : 'line-through'
41847                             },
41848                             '.course-timesheet .course-highlight' : {
41849                                 'border-top-style': 'dashed !important',
41850                                 'border-bottom-bottom': 'dashed !important'
41851                             },
41852                             '.course-timesheet .course-item' : {
41853                                 'font-family'   : 'tahoma, arial, helvetica',
41854                                 'font-size'     : '11px',
41855                                 'overflow'      : 'hidden',
41856                                 'padding-left'  : '10px',
41857                                 'padding-right' : '10px',
41858                                 'padding-top' : '10px' 
41859                             }
41860                             
41861                         }, Roo.id());
41862                                 this.ds.load({});
41863                     }
41864                 },
41865                 autoWidth : true,
41866                 monitorWindowResize : false,
41867                 cellrenderer : function(v,x,r)
41868                 {
41869                     return v;
41870                 },
41871                 sm : {
41872                     xtype: 'CellSelectionModel',
41873                     xns: Roo.grid
41874                 },
41875                 dataSource : {
41876                     xtype: 'Store',
41877                     xns: Roo.data,
41878                     listeners : {
41879                         beforeload : function (_self, options)
41880                         {
41881                             options.params = options.params || {};
41882                             options.params._month = _this.monthField.getValue();
41883                             options.params.limit = 9999;
41884                             options.params['sort'] = 'when_dt';    
41885                             options.params['dir'] = 'ASC';    
41886                             this.proxy.loadResponse = this.loadResponse;
41887                             Roo.log("load?");
41888                             //this.addColumns();
41889                         },
41890                         load : function (_self, records, options)
41891                         {
41892                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41893                                 // if you click on the translation.. you can edit it...
41894                                 var el = Roo.get(this);
41895                                 var id = el.dom.getAttribute('data-id');
41896                                 var d = el.dom.getAttribute('data-date');
41897                                 var t = el.dom.getAttribute('data-time');
41898                                 //var id = this.child('span').dom.textContent;
41899                                 
41900                                 //Roo.log(this);
41901                                 Pman.Dialog.CourseCalendar.show({
41902                                     id : id,
41903                                     when_d : d,
41904                                     when_t : t,
41905                                     productitem_active : id ? 1 : 0
41906                                 }, function() {
41907                                     _this.grid.ds.load({});
41908                                 });
41909                            
41910                            });
41911                            
41912                            _this.panel.fireEvent('resize', [ '', '' ]);
41913                         }
41914                     },
41915                     loadResponse : function(o, success, response){
41916                             // this is overridden on before load..
41917                             
41918                             Roo.log("our code?");       
41919                             //Roo.log(success);
41920                             //Roo.log(response)
41921                             delete this.activeRequest;
41922                             if(!success){
41923                                 this.fireEvent("loadexception", this, o, response);
41924                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41925                                 return;
41926                             }
41927                             var result;
41928                             try {
41929                                 result = o.reader.read(response);
41930                             }catch(e){
41931                                 Roo.log("load exception?");
41932                                 this.fireEvent("loadexception", this, o, response, e);
41933                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41934                                 return;
41935                             }
41936                             Roo.log("ready...");        
41937                             // loop through result.records;
41938                             // and set this.tdate[date] = [] << array of records..
41939                             _this.tdata  = {};
41940                             Roo.each(result.records, function(r){
41941                                 //Roo.log(r.data);
41942                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41943                                     _this.tdata[r.data.when_dt.format('j')] = [];
41944                                 }
41945                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41946                             });
41947                             
41948                             //Roo.log(_this.tdata);
41949                             
41950                             result.records = [];
41951                             result.totalRecords = 6;
41952                     
41953                             // let's generate some duumy records for the rows.
41954                             //var st = _this.dateField.getValue();
41955                             
41956                             // work out monday..
41957                             //st = st.add(Date.DAY, -1 * st.format('w'));
41958                             
41959                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41960                             
41961                             var firstOfMonth = date.getFirstDayOfMonth();
41962                             var days = date.getDaysInMonth();
41963                             var d = 1;
41964                             var firstAdded = false;
41965                             for (var i = 0; i < result.totalRecords ; i++) {
41966                                 //var d= st.add(Date.DAY, i);
41967                                 var row = {};
41968                                 var added = 0;
41969                                 for(var w = 0 ; w < 7 ; w++){
41970                                     if(!firstAdded && firstOfMonth != w){
41971                                         continue;
41972                                     }
41973                                     if(d > days){
41974                                         continue;
41975                                     }
41976                                     firstAdded = true;
41977                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41978                                     row['weekday'+w] = String.format(
41979                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41980                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41981                                                     d,
41982                                                     date.format('Y-m-')+dd
41983                                                 );
41984                                     added++;
41985                                     if(typeof(_this.tdata[d]) != 'undefined'){
41986                                         Roo.each(_this.tdata[d], function(r){
41987                                             var is_sub = '';
41988                                             var deactive = '';
41989                                             var id = r.id;
41990                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41991                                             if(r.parent_id*1>0){
41992                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41993                                                 id = r.parent_id;
41994                                             }
41995                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41996                                                 deactive = 'de-act-link';
41997                                             }
41998                                             
41999                                             row['weekday'+w] += String.format(
42000                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42001                                                     id, //0
42002                                                     r.product_id_name, //1
42003                                                     r.when_dt.format('h:ia'), //2
42004                                                     is_sub, //3
42005                                                     deactive, //4
42006                                                     desc // 5
42007                                             );
42008                                         });
42009                                     }
42010                                     d++;
42011                                 }
42012                                 
42013                                 // only do this if something added..
42014                                 if(added > 0){ 
42015                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
42016                                 }
42017                                 
42018                                 
42019                                 // push it twice. (second one with an hour..
42020                                 
42021                             }
42022                             //Roo.log(result);
42023                             this.fireEvent("load", this, o, o.request.arg);
42024                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42025                         },
42026                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42027                     proxy : {
42028                         xtype: 'HttpProxy',
42029                         xns: Roo.data,
42030                         method : 'GET',
42031                         url : baseURL + '/Roo/Shop_course.php'
42032                     },
42033                     reader : {
42034                         xtype: 'JsonReader',
42035                         xns: Roo.data,
42036                         id : 'id',
42037                         fields : [
42038                             {
42039                                 'name': 'id',
42040                                 'type': 'int'
42041                             },
42042                             {
42043                                 'name': 'when_dt',
42044                                 'type': 'string'
42045                             },
42046                             {
42047                                 'name': 'end_dt',
42048                                 'type': 'string'
42049                             },
42050                             {
42051                                 'name': 'parent_id',
42052                                 'type': 'int'
42053                             },
42054                             {
42055                                 'name': 'product_id',
42056                                 'type': 'int'
42057                             },
42058                             {
42059                                 'name': 'productitem_id',
42060                                 'type': 'int'
42061                             },
42062                             {
42063                                 'name': 'guid',
42064                                 'type': 'int'
42065                             }
42066                         ]
42067                     }
42068                 },
42069                 toolbar : {
42070                     xtype: 'Toolbar',
42071                     xns: Roo,
42072                     items : [
42073                         {
42074                             xtype: 'Button',
42075                             xns: Roo.Toolbar,
42076                             listeners : {
42077                                 click : function (_self, e)
42078                                 {
42079                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42080                                     sd.setMonth(sd.getMonth()-1);
42081                                     _this.monthField.setValue(sd.format('Y-m-d'));
42082                                     _this.grid.ds.load({});
42083                                 }
42084                             },
42085                             text : "Back"
42086                         },
42087                         {
42088                             xtype: 'Separator',
42089                             xns: Roo.Toolbar
42090                         },
42091                         {
42092                             xtype: 'MonthField',
42093                             xns: Roo.form,
42094                             listeners : {
42095                                 render : function (_self)
42096                                 {
42097                                     _this.monthField = _self;
42098                                    // _this.monthField.set  today
42099                                 },
42100                                 select : function (combo, date)
42101                                 {
42102                                     _this.grid.ds.load({});
42103                                 }
42104                             },
42105                             value : (function() { return new Date(); })()
42106                         },
42107                         {
42108                             xtype: 'Separator',
42109                             xns: Roo.Toolbar
42110                         },
42111                         {
42112                             xtype: 'TextItem',
42113                             xns: Roo.Toolbar,
42114                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42115                         },
42116                         {
42117                             xtype: 'Fill',
42118                             xns: Roo.Toolbar
42119                         },
42120                         {
42121                             xtype: 'Button',
42122                             xns: Roo.Toolbar,
42123                             listeners : {
42124                                 click : function (_self, e)
42125                                 {
42126                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42127                                     sd.setMonth(sd.getMonth()+1);
42128                                     _this.monthField.setValue(sd.format('Y-m-d'));
42129                                     _this.grid.ds.load({});
42130                                 }
42131                             },
42132                             text : "Next"
42133                         }
42134                     ]
42135                 },
42136                  
42137             }
42138         };
42139         
42140         *//*
42141  * Based on:
42142  * Ext JS Library 1.1.1
42143  * Copyright(c) 2006-2007, Ext JS, LLC.
42144  *
42145  * Originally Released Under LGPL - original licence link has changed is not relivant.
42146  *
42147  * Fork - LGPL
42148  * <script type="text/javascript">
42149  */
42150  
42151 /**
42152  * @class Roo.LoadMask
42153  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42154  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42155  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42156  * element's UpdateManager load indicator and will be destroyed after the initial load.
42157  * @constructor
42158  * Create a new LoadMask
42159  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42160  * @param {Object} config The config object
42161  */
42162 Roo.LoadMask = function(el, config){
42163     this.el = Roo.get(el);
42164     Roo.apply(this, config);
42165     if(this.store){
42166         this.store.on('beforeload', this.onBeforeLoad, this);
42167         this.store.on('load', this.onLoad, this);
42168         this.store.on('loadexception', this.onLoadException, this);
42169         this.removeMask = false;
42170     }else{
42171         var um = this.el.getUpdateManager();
42172         um.showLoadIndicator = false; // disable the default indicator
42173         um.on('beforeupdate', this.onBeforeLoad, this);
42174         um.on('update', this.onLoad, this);
42175         um.on('failure', this.onLoad, this);
42176         this.removeMask = true;
42177     }
42178 };
42179
42180 Roo.LoadMask.prototype = {
42181     /**
42182      * @cfg {Boolean} removeMask
42183      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42184      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42185      */
42186     /**
42187      * @cfg {String} msg
42188      * The text to display in a centered loading message box (defaults to 'Loading...')
42189      */
42190     msg : 'Loading...',
42191     /**
42192      * @cfg {String} msgCls
42193      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42194      */
42195     msgCls : 'x-mask-loading',
42196
42197     /**
42198      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42199      * @type Boolean
42200      */
42201     disabled: false,
42202
42203     /**
42204      * Disables the mask to prevent it from being displayed
42205      */
42206     disable : function(){
42207        this.disabled = true;
42208     },
42209
42210     /**
42211      * Enables the mask so that it can be displayed
42212      */
42213     enable : function(){
42214         this.disabled = false;
42215     },
42216     
42217     onLoadException : function()
42218     {
42219         Roo.log(arguments);
42220         
42221         if (typeof(arguments[3]) != 'undefined') {
42222             Roo.MessageBox.alert("Error loading",arguments[3]);
42223         } 
42224         /*
42225         try {
42226             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42227                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42228             }   
42229         } catch(e) {
42230             
42231         }
42232         */
42233     
42234         
42235         
42236         this.el.unmask(this.removeMask);
42237     },
42238     // private
42239     onLoad : function()
42240     {
42241         this.el.unmask(this.removeMask);
42242     },
42243
42244     // private
42245     onBeforeLoad : function(){
42246         if(!this.disabled){
42247             this.el.mask(this.msg, this.msgCls);
42248         }
42249     },
42250
42251     // private
42252     destroy : function(){
42253         if(this.store){
42254             this.store.un('beforeload', this.onBeforeLoad, this);
42255             this.store.un('load', this.onLoad, this);
42256             this.store.un('loadexception', this.onLoadException, this);
42257         }else{
42258             var um = this.el.getUpdateManager();
42259             um.un('beforeupdate', this.onBeforeLoad, this);
42260             um.un('update', this.onLoad, this);
42261             um.un('failure', this.onLoad, this);
42262         }
42263     }
42264 };/*
42265  * Based on:
42266  * Ext JS Library 1.1.1
42267  * Copyright(c) 2006-2007, Ext JS, LLC.
42268  *
42269  * Originally Released Under LGPL - original licence link has changed is not relivant.
42270  *
42271  * Fork - LGPL
42272  * <script type="text/javascript">
42273  */
42274
42275
42276 /**
42277  * @class Roo.XTemplate
42278  * @extends Roo.Template
42279  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42280 <pre><code>
42281 var t = new Roo.XTemplate(
42282         '&lt;select name="{name}"&gt;',
42283                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42284         '&lt;/select&gt;'
42285 );
42286  
42287 // then append, applying the master template values
42288  </code></pre>
42289  *
42290  * Supported features:
42291  *
42292  *  Tags:
42293
42294 <pre><code>
42295       {a_variable} - output encoded.
42296       {a_variable.format:("Y-m-d")} - call a method on the variable
42297       {a_variable:raw} - unencoded output
42298       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42299       {a_variable:this.method_on_template(...)} - call a method on the template object.
42300  
42301 </code></pre>
42302  *  The tpl tag:
42303 <pre><code>
42304         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42305         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42306         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42307         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42308   
42309         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42310         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42311 </code></pre>
42312  *      
42313  */
42314 Roo.XTemplate = function()
42315 {
42316     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42317     if (this.html) {
42318         this.compile();
42319     }
42320 };
42321
42322
42323 Roo.extend(Roo.XTemplate, Roo.Template, {
42324
42325     /**
42326      * The various sub templates
42327      */
42328     tpls : false,
42329     /**
42330      *
42331      * basic tag replacing syntax
42332      * WORD:WORD()
42333      *
42334      * // you can fake an object call by doing this
42335      *  x.t:(test,tesT) 
42336      * 
42337      */
42338     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42339
42340     /**
42341      * compile the template
42342      *
42343      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42344      *
42345      */
42346     compile: function()
42347     {
42348         var s = this.html;
42349      
42350         s = ['<tpl>', s, '</tpl>'].join('');
42351     
42352         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42353             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42354             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42355             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42356             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42357             m,
42358             id     = 0,
42359             tpls   = [];
42360     
42361         while(true == !!(m = s.match(re))){
42362             var forMatch   = m[0].match(nameRe),
42363                 ifMatch   = m[0].match(ifRe),
42364                 execMatch   = m[0].match(execRe),
42365                 namedMatch   = m[0].match(namedRe),
42366                 
42367                 exp  = null, 
42368                 fn   = null,
42369                 exec = null,
42370                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42371                 
42372             if (ifMatch) {
42373                 // if - puts fn into test..
42374                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42375                 if(exp){
42376                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42377                 }
42378             }
42379             
42380             if (execMatch) {
42381                 // exec - calls a function... returns empty if true is  returned.
42382                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42383                 if(exp){
42384                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42385                 }
42386             }
42387             
42388             
42389             if (name) {
42390                 // for = 
42391                 switch(name){
42392                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42393                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42394                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42395                 }
42396             }
42397             var uid = namedMatch ? namedMatch[1] : id;
42398             
42399             
42400             tpls.push({
42401                 id:     namedMatch ? namedMatch[1] : id,
42402                 target: name,
42403                 exec:   exec,
42404                 test:   fn,
42405                 body:   m[1] || ''
42406             });
42407             if (namedMatch) {
42408                 s = s.replace(m[0], '');
42409             } else { 
42410                 s = s.replace(m[0], '{xtpl'+ id + '}');
42411             }
42412             ++id;
42413         }
42414         this.tpls = [];
42415         for(var i = tpls.length-1; i >= 0; --i){
42416             this.compileTpl(tpls[i]);
42417             this.tpls[tpls[i].id] = tpls[i];
42418         }
42419         this.master = tpls[tpls.length-1];
42420         return this;
42421     },
42422     /**
42423      * same as applyTemplate, except it's done to one of the subTemplates
42424      * when using named templates, you can do:
42425      *
42426      * var str = pl.applySubTemplate('your-name', values);
42427      *
42428      * 
42429      * @param {Number} id of the template
42430      * @param {Object} values to apply to template
42431      * @param {Object} parent (normaly the instance of this object)
42432      */
42433     applySubTemplate : function(id, values, parent)
42434     {
42435         
42436         
42437         var t = this.tpls[id];
42438         
42439         
42440         try { 
42441             if(t.test && !t.test.call(this, values, parent)){
42442                 return '';
42443             }
42444         } catch(e) {
42445             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42446             Roo.log(e.toString());
42447             Roo.log(t.test);
42448             return ''
42449         }
42450         try { 
42451             
42452             if(t.exec && t.exec.call(this, values, parent)){
42453                 return '';
42454             }
42455         } catch(e) {
42456             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42457             Roo.log(e.toString());
42458             Roo.log(t.exec);
42459             return ''
42460         }
42461         try {
42462             var vs = t.target ? t.target.call(this, values, parent) : values;
42463             parent = t.target ? values : parent;
42464             if(t.target && vs instanceof Array){
42465                 var buf = [];
42466                 for(var i = 0, len = vs.length; i < len; i++){
42467                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42468                 }
42469                 return buf.join('');
42470             }
42471             return t.compiled.call(this, vs, parent);
42472         } catch (e) {
42473             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42474             Roo.log(e.toString());
42475             Roo.log(t.compiled);
42476             return '';
42477         }
42478     },
42479
42480     compileTpl : function(tpl)
42481     {
42482         var fm = Roo.util.Format;
42483         var useF = this.disableFormats !== true;
42484         var sep = Roo.isGecko ? "+" : ",";
42485         var undef = function(str) {
42486             Roo.log("Property not found :"  + str);
42487             return '';
42488         };
42489         
42490         var fn = function(m, name, format, args)
42491         {
42492             //Roo.log(arguments);
42493             args = args ? args.replace(/\\'/g,"'") : args;
42494             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42495             if (typeof(format) == 'undefined') {
42496                 format= 'htmlEncode';
42497             }
42498             if (format == 'raw' ) {
42499                 format = false;
42500             }
42501             
42502             if(name.substr(0, 4) == 'xtpl'){
42503                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42504             }
42505             
42506             // build an array of options to determine if value is undefined..
42507             
42508             // basically get 'xxxx.yyyy' then do
42509             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42510             //    (function () { Roo.log("Property not found"); return ''; })() :
42511             //    ......
42512             
42513             var udef_ar = [];
42514             var lookfor = '';
42515             Roo.each(name.split('.'), function(st) {
42516                 lookfor += (lookfor.length ? '.': '') + st;
42517                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42518             });
42519             
42520             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42521             
42522             
42523             if(format && useF){
42524                 
42525                 args = args ? ',' + args : "";
42526                  
42527                 if(format.substr(0, 5) != "this."){
42528                     format = "fm." + format + '(';
42529                 }else{
42530                     format = 'this.call("'+ format.substr(5) + '", ';
42531                     args = ", values";
42532                 }
42533                 
42534                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42535             }
42536              
42537             if (args.length) {
42538                 // called with xxyx.yuu:(test,test)
42539                 // change to ()
42540                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42541             }
42542             // raw.. - :raw modifier..
42543             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42544             
42545         };
42546         var body;
42547         // branched to use + in gecko and [].join() in others
42548         if(Roo.isGecko){
42549             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42550                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42551                     "';};};";
42552         }else{
42553             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42554             body.push(tpl.body.replace(/(\r\n|\n)/g,
42555                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42556             body.push("'].join('');};};");
42557             body = body.join('');
42558         }
42559         
42560         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42561        
42562         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42563         eval(body);
42564         
42565         return this;
42566     },
42567
42568     applyTemplate : function(values){
42569         return this.master.compiled.call(this, values, {});
42570         //var s = this.subs;
42571     },
42572
42573     apply : function(){
42574         return this.applyTemplate.apply(this, arguments);
42575     }
42576
42577  });
42578
42579 Roo.XTemplate.from = function(el){
42580     el = Roo.getDom(el);
42581     return new Roo.XTemplate(el.value || el.innerHTML);
42582 };